作者:张洛丹
爱可生 DBA 团队成员,主要负责 MySQL 故障处理和公司自动化运维平台维护。对技术执着,为客户负责。
本文来源:原创投稿
*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
对于 MySQL Online DDL 目前主流的有三种工具:
本文主要讲解 pt-online-schema-change 的使用以及三种工具的简单对比。
1.1 原理
1. 创建一个与原表结构相同的空表,表名是 _new 后缀;
2. 修改步骤 1 创建的空表的表结构;
3. 在原表上加三个触发器:delete/update/insert,用于 copy 数据过程中,将原表中要执行的语句在新表中执行;
4. 将原表数据以数据块(chunk)的形式 copy 到新表;
5. rename 原表为 old 表,并把新表 rename 为原表名,然后删除旧表;
6. 删除触发器。
1.2 限制
1. 原表上要有 primary key 或 unique index,因为当执行该工具时会创建一个 DELETE 触发器来更新新表;
注意:一个例外的情况是 --alter 指定的子句中是在原表中的列上创建 primary key 或 unique index,这种情况下将使用这些列用于 DELETE 触发器。
2. 不能使用 rename 子句来重命名表;
3. 列不能通过删除 + 添加的方式来重命名,这样将不会 copy 原有列的数据到新列;
4. 如果要添加的列是 not null,则必须指定默认值,否则会执行失败;
5. 删除外键约束(DROP FOREIGN KEY constraint_name),外键约束名前面必须添加一个下划线 '_',即需要指定名称 _constraint_name,而不是原始的 constraint_name;
例如:
CONSTRAINT `fk_foo` FOREIGN KEY (`foo_id`) REFERENCES `bar` (`foo_id`)
必须指定 --alter "DROP FOREIGN KEY _fk_foo"。
2.1 语法
pt-online-schema-change [OPTIONS] DSN
其中 DSN 是指 Data Source Name,是连接数据库的变量信息。格式为 key=value。
DSN 的 key 有:
2.2 参数字典(文末)
3.1 安装
-- 安装 yum 仓库
yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm
-- 安装 percona toolkit
yum install percona-toolkit -y
3.2 使用示例
本示例模拟修改列类型,将列类型由 char(20) 修改为 varchar(200)
版本信息:MySQL 5.7.25,percona-tool 3.2.0
数据量 200 万
准备
3.2.1 创建用户
GRANT SELECT, INSERT, UPDATE, DELETE,
CREATE, DROP, PROCESS, REFERENCES,
INDEX, ALTER, SUPER, LOCK TABLES,
REPLICATION SLAVE, TRIGGER
ON *.* TO 'ptosc'@'%'
3.2.2 写 ALTER 语句
modify c varchar(200) not null default ""
3.2.3 环境检查
说明:工具在执行时也会进行检查,如果遇到不能执行的情况,则报错,建议在执行前先进行 dry-run。
3.2.3.1 检查要变更的表上是否有主键或非空唯一键
mysql> desc sbtest1;
+-------+-----------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |
+-------+-----------+------+-----+---------+----------------+
|
| k | int(11) | NO | MUL | 0 | |
|
| pad | char(60) | NO | | | |
+-------+-----------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
3.2.3.2 检查是否有其他表外键引用该表
select * from information_schema.key_column_usage where referenced_table_schema='testdb' and referenced_table_name='sbtest1'G
*************************** 1. row ***************************
CONSTRAINT_CATALOG: def
CONSTRAINT_SCHEMA: testdb
CONSTRAINT_NAME: test2_ibfk_1
TABLE_CATALOG: def
TABLE_SCHEMA: testdb
TABLE_NAME: test2
COLUMN_NAME: t_id
ORDINAL_POSITION: 1
POSITION_IN_UNIQUE_CONSTRAINT: 1
REFERENCED_TABLE_SCHEMA: testdb
REFERENCED_TABLE_NAME: sbtest1
REFERENCED_COLUMN_NAME: id
1 row in set (0.01 sec)
若有,则需要使用 --alter-foreign-keys-method 选项
3.2.3.3 检查表上是否有触发器
mysql> select * from information_schema.triggers where event_object_schema='testdb' and event_object_table='sbtest1'G
*************************** 1. row ***************************
TRIGGER_CATALOG: def
TRIGGER_SCHEMA: testdb
TRIGGER_NAME: trig1
EVENT_MANIPULATION: INSERT
EVENT_OBJECT_CATALOG: def
EVENT_OBJECT_SCHEMA: testdb
EVENT_OBJECT_TABLE: sbtest1
ACTION_ORDER: 1
ACTION_CONDITION: NULL
ACTION_STATEMENT: INSERT INTO time VALUES(NOW())
ACTION_ORIENTATION: ROW
ACTION_TIMING: AFTER
ACTION_REFERENCE_OLD_TABLE: NULL
ACTION_REFERENCE_NEW_TABLE: NULL
ACTION_REFERENCE_OLD_ROW: OLD
ACTION_REFERENCE_NEW_ROW: NEW
CREATED: 2020-08-23 10:43:27.38
SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
DEFINER: root@localhost
CHARACTER_SET_CLIENT: utf8
COLLATION_CONNECTION: utf8_general_ci
DATABASE_COLLATION: utf8mb4_bin
1 row in set (0.00 sec)
若有,则需指定 --preserve-triggers 选项,且在 percona tool 3.0.4 起,对于 MySQL 5.7.2 以上,支持原表上有触发器,建议使用前在测试环境进行测试。
官方 issue 链接:https://jira.percona.com/browse/PT-91
3.2.3.4 检查从库是否设置 change filter
show slave statusG
... Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table:
如果设置了 change filter,则不会执行,除非指定 --no-check-replication-filters
3.2.4 执行 dry run
# pt-online-schema-change --print --statistics
--progress time,30 --preserve-triggers --user=ptosc
--password=ptosc --alter 'modify c varchar(200) not null default ""'
h=127.0.1.1,P=3306,D=testdb,t=sbtest1
--pause-file=/tmp/aa.txt --max-load=threads_running=100,threads_connected=200
--critical-load=threads_running=1000 --chunk-size=1000
--alter-foreign-keys-method auto --dry-runOperation, tries, wait: analyze_table, 10, 1
copy_rows, 10, 0.25
create_triggers, 10, 1
drop_triggers, 10, 1
swap_tables, 10, 1
update_foreign_keys, 10, 1
Child tables: `testdb`.`test2` (Approx. 1 rows)
Will automatically choose the method to update foreign keys.Starting a dry run. `testdb`.`sbtest1` will not be altered. Specify --execute instead of --dry-run to alter the table.
Creating new table...
CREATE TABLE `testdb`.`_sbtest1_new` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` char(120) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`pad` char(60) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
Created new table testdb._sbtest1_new OK.
Altering new table...
ALTER TABLE `testdb`.`_sbtest1_new` modify c varchar(200) not null default ""
Altered `testdb`.`_sbtest1_new` OK.
Not creating triggers because this is a dry run.
Not copying rows because this is a dry run.
INSERT LOW_PRIORITY IGNORE INTO `testdb`.`_sbtest1_new` (`id`, `k`, `c`, `pad`) SELECT `id`, `k`, `c`, `pad` FROM `testdb`.`sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) AND ((`id` <= ?)) LOCK IN SHARE MODE /*pt-online-schema-change 6337 copy nibble*/
SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `testdb`.`sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) ORDER BY `id` LIMIT ?, 2 /*next chunk boundary*/
Not determining the method to update foreign keys because this is a dry run.
2020-08-23T13:24:19 Adding original triggers to new table.
Not swapping tables because this is a dry run.
Not updating foreign key constraints because this is a dry run.
Not dropping old table because this is a dry run.
Not dropping triggers because this is a dry run.
DROP TRIGGER IF EXISTS `testdb`.`pt_osc_testdb_sbtest1_del`
DROP TRIGGER IF EXISTS `testdb`.`pt_osc_testdb_sbtest1_upd`
DROP TRIGGER IF EXISTS `testdb`.`pt_osc_testdb_sbtest1_ins`
2020-08-23T13:24:19 Dropping new table...
DROP TABLE IF EXISTS `testdb`.`_sbtest1_new`;
2020-08-23T13:24:19 Dropped new table OK.
# Event Count# ====== =====# INSERT 0
Dry run complete. `testdb`.`sbtest1` was not altered.
3.2.5 执行
将 --dry-run 修改为 --execute
# pt-online-schema-change --print --statistics
--progress time,30 --preserve-triggers --user=ptosc
--password=ptosc --alter 'modify c varchar(200) not null default ""'
h=127.0.1.1,P=3306,D=testdb,t=sbtest1
--pause-file=/tmp/aa.txt --max-load=threads_running=100,threads_connected=200
--critical-load=threads_running=1000 --chunk-size=1000
--alter-foreign-keys-method auto --executeFound 2 slaves:
10-186-64-51 -> 10.186.64.51:3306
10-186-64-48 -> 10.186.64.48:3306
Will check slave lag on:10-186-64-51 -> 10.186.64.51:3306
10-186-64-48 -> 10.186.64.48:3306
Operation, tries, wait: analyze_table, 10, 1
copy_rows, 10, 0.25
create_triggers, 10, 1
drop_triggers, 10, 1
swap_tables, 10, 1
update_foreign_keys, 10, 1
Child tables: `testdb`.`test2` (approx. 1 rows)
Will automatically choose the method to update foreign keys.Altering `testdb`.`sbtest1`...
Creating new table...
CREATE TABLE `testdb`.`_sbtest1_new` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` char(120) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`pad` char(60) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
Created new table testdb._sbtest1_new OK.
Altering new table...
ALTER TABLE `testdb`.`_sbtest1_new` modify c varchar(200) not null default ""
Altered `testdb`.`_sbtest1_new` OK.
2020-08-23T14:44:53 Creating triggers...
2020-08-23T14:44:53 Created triggers OK.
2020-08-23T14:44:53 Copying approximately 1972656 rows...
INSERT LOW_PRIORITY IGNORE INTO `testdb`.`_sbtest1_new` (`id`, `k`, `c`, `pad`) SELECT `id`, `k`, `c`, `pad` FROM `testdb`.`sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) AND ((`id` <= ?)) LOCK IN SHARE MODE /*pt-online-schema-change 15822 copy nibble*/
SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `testdb`.`sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) ORDER BY `id` LIMIT ?, 2 /*next chunk boundary*/
Copying `testdb`.`sbtest1`: 52% 00:27 remain
Copying `testdb`.`sbtest1`: 99% 00:00 remain
2020-08-23T14:45:53 Copied rows OK.
2020-08-23T14:45:53 Max rows for the rebuild_constraints method: 4000
Determining the method to update foreign keys...
2020-08-23T14:45:53 `testdb`.`test2`: 1 rows; can use rebuild_constraints
2020-08-23T14:45:53 Adding original triggers to new table.
2020-08-23T14:45:53 Analyzing new table...
2020-08-23T14:45:53 Swapping tables...
RENAME TABLE `testdb`.`sbtest1` TO `testdb`.`_sbtest1_old`, `testdb`.`_sbtest1_new` TO `testdb`.`sbtest1`
2020-08-23T14:45:54 Swapped original and new tables OK.
2020-08-23T14:45:54 Rebuilding foreign key constraints...
ALTER TABLE `testdb`.`test2` DROP FOREIGN KEY `test2_ibfk_1`, ADD CONSTRAINT `_test2_ibfk_1` FOREIGN KEY (`t_id`) REFERENCES `testdb`.`sbtest1` (`id`)
2020-08-23T14:45:54 Rebuilt foreign key constraints OK.
2020-08-23T14:45:54 Dropping old table...
DROP TABLE IF EXISTS `testdb`.`_sbtest1_old`
2020-08-23T14:45:54 Dropped old table `testdb`.`_sbtest1_old` OK.
2020-08-23T14:45:54 Dropping triggers...
DROP TRIGGER IF EXISTS `testdb`.`pt_osc_testdb_sbtest1_del`
DROP TRIGGER IF EXISTS `testdb`.`pt_osc_testdb_sbtest1_upd`
DROP TRIGGER IF EXISTS `testdb`.`pt_osc_testdb_sbtest1_ins`
2020-08-23T14:45:54 Dropped triggers OK.
# Event Count# ================== =====# INSERT 2000
# rebuilt_constraint 1
Successfully altered `testdb`.`sbtest1`.
如上,输出比较简单,包括了每一步执行的 SQL。copy 数据期间打印了 copy 的进度以及预计剩余时间;最后打印出统计信息,比如 insert 的数据块数。
3.2.6 执行后检查
3.2.6.1 检查原表是否正确修改
mysql> show create table sbtest1G
*************************** 1. row ***************************
Table: sbtest1
Create Table: CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(200) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`pad` char(60) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
1 row in set (0.00 sec)
mysql> desc sbtest1;+-------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| k | int(11) | NO | MUL | 0 | |
| c | varchar(200) | NO | | | |
| pad | char(60) | NO | | | |
+-------+--------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)
3.2.6.2 检查引用该表的外键
mysql> show create table test2G
*************************** 1. row ***************************
Table: test2Create Table: CREATE TABLE `test2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`t_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `_test2_ibfk_1` (`t_id`),
CONSTRAINT `_test2_ibfk_1` FOREIGN KEY (`t_id`) REFERENCES `sbtest1` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin1 row in set (0.00 sec)
3.2.6.3 检查原表上触发器
mysql> show triggersG
*************************** 1. row ***************************
Trigger: trig1 Event: INSERT Table: sbtest1 Statement: INSERT INTO time VALUES(NOW()) Timing: AFTER Created: 2020-08-23 14:45:53.96 sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
Definer: root@localhostcharacter_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8mb4_bin
1 row in set (0.00 sec)
4.1 原理对比
这里简单说一下另外两个工具:原生 Online DDL 和 gh-ost 的原理。
4.1.1 MySQL 原生 DDL
自 MySQL 5.6 起,MySQL 原生支持 Online DDL,即在执行 DDL 期间允许执行 DML(insert、update、delete)。了解 Online DDL 先了解一下之前 DDL 的 2 种算法 copy 和 inplace。
Copy:
1. 按照原表定义创建一个新的临时表
2. 对原表加写锁(禁止 DML,允许 select)
3. 步骤 1)建立的临时表执行 DDL
4. 将原表中的数据 copy 到临时表
5. 释放原表的写锁
6. 将原表删除,并将临时表重命名为原表
可见,采用 copy 方式期间需要锁表,禁止 DML,因此是非 Online 的。比如:删除主键、修改列类型、修改字符集,这些操作会导致行记录格式发生变化(无法通过全量 + 增量实现 Online)。
Inplace:
在原表上进行更改,不需要生成临时表,不需要进行数据 copy 的过程。
根据是否行记录格式,分为两类:
对于 rebuild 方式实现 Online 是通过缓存 DDL 期间的 DML,待 DDL 完成之后,将 DML 应用到表上来实现的。例如,执行一个 alter table A engine=InnoDB; 重建表的 DDL 其大致流程如下:
1. 建立一个临时文件,扫描表 A 主键的所有数据页;
2. 用数据页中表 A 的记录生成 B+ 树,存储到临时文件中;
3. 生成临时文件的过程中,将所有对 A 的操作记录在一个日志文件(row log)中;
4. 临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表 A 相同的数据文件;
5. 用临时文件替换表 A 的数据文件。
说明:
1. 在 copy 数据到新表期间,在原表上是加的 MDL 读锁(允许 DML,禁止 DDL)
2. 在应用增量期间对原表加 MDL 写锁(禁止 DML 和 DDL)
3. 根据表A重建出来的数据是放在 tmp_file 里的,这个临时文件是 InnoDB 在内部创建出来的,整个 DDL 过程都在 InnoDB 内部完成。对于 server 层来说,没有把数据挪动到临时表,是一个原地操作,这就是“inplace”名称的来源。
4.1.2 gh-ost
主要原理如下:
1. 创建幽灵表:_xxx_gho(和原表结构一致),_xxx_ghc(用于记录变更日志)
2. 在步骤 1 中创建的幽灵表上执行 DDL 语句
3. 模拟成备库连接到真正的主库或备库
4. 进行切换(cut-over)
关于 gh-ost 的详细使用方式可以看看这篇文章《Online DDL工具 gh-ost》
https://mp.weixin.qq.com/s/V3mfuv8EP8UB1fwtfVRHuQ
4.2 如何选择
从原理中,可以看出几个关键点:
因此,总结以下几个选择工具的判断依据:
1. 如果 MySQL 版本是 5.6 之前,不支持 Online DDL,选用第三方工具 pt-osc 或 gh-ost;
2. 如果 MySQL 版本是 5.6 以上,对于使用 copy table 方式的 DDL,不支持 Online,使用第三方工具 pt-osc 或 gh-ost;
3. 对于可以使用 Inplace no-rebuild 方式的 DDL,使用原生 Online DDL;
4. 对于使用 Inplace rebuild table 方式的 DDL,如果想使 DDL 过程更加可控,且对从库延迟比较敏感,使用第三方工具 pt-osc 或 gh-ost,否则使用原生 Online DDL;
5. 对于想减少对主库的影响,实时交互,可以选用 gh-ost;
① 常用基本
--dry-run
--execute
--user, -u
--password, -p
--host, -h
--port, -P
--socket
--ask-pass
--alter “string”
--database, -D
② 控制输出形式
--progress
--quiet, - q
--statistics
③ 表上行为控制
--alter-foreign-keys-method "string"
当该工具重命名原始表以让新表取而代之时,外键跟随被重命名的表,因此必须更改外键以引用新表。
支持两种方式:rebuild_constraints 和 drop_swap 。
可选值:
auto:
rebuild_constraints
此方法使用 ALTER TABLE 删除并重新添加引用新表的外键约束。这是首选的方式,除非子表(引用 DDL 表中列的表)太大,更改会花费太长时间。
通过比较子表的行数和将行从旧表复制到新表的速度来确定是否使用该方式。
说明:
由于 MySQL 中的限制,外键在更改后不能与之前的名称相同。该工具在重新定义外键时必须重命名外键,通常在名称中添加一个前导下划线 '_' 。在某些情况下,MySQL 还会自动重命名外键所需的索引。
drop_swap
禁用外键检查(FOREIGH_KEY_CHECKS=0),先删除原始表,然后将新表重命名到原来的位置。这与交换新旧表的方法不同,后者使用的是客户端应用程序无法检测到的原子 RENAME。
none
这种方式和 drop_swap 类似,但是没有 swap。任何引用原表的外键将会指向一个不存在的表,这样会造成外键违规,在 show engine innodb status 中将会有类似下面的输出:
Trying to add to index `idx_fk_staff_id` tuple:
DATA TUPLE: 2 fields;
0: len 1; hex 05; asc ;;
1: len 4; hex 80000001; asc ;;
But the parent table `sakila`.`staff_old`
or its .ibd file does not currently exist!
这是因为原始表(在本例中为 sakila.staff)被重命名为 sakila.staff_old,然后 drop 掉了。提供了这种处理外键约束的方法,以便数据库管理员可以根据需要禁用该工具的内置功能。
--only-same-schema-fks
--null-to-not-null
--[no]analyze-before-swap
说明:innodb_stats_persistent 为 ON,表示统计信息会持久化存储,OFF 表示统计信息只存储在内存。
--[no]drop-new-table
--[no]drop-old-table
--[no]swap-tables
使用 --no-swap-tables 会运行整个过程,它会创建新表,复制所有行但最后会删除新表。它的目的是运行一个更现实的演练。
--[no]drop-triggers
--preserve-triggers
例如:
CREATE TABLE test.t1 (
id INT NOT NULL AUTO_INCREMENT,
f1 INT,
f2 VARCHAR(32),
PRIMARY KEY (id)
);CREATE TABLE test.log (
ts TIMESTAMP,
msg VARCHAR(255)
);CREATE TRIGGER test.after_update
AFTER
UPDATE ON test.t1
FOR EACH ROW
INSERT INTO test.log
VALUES (NOW(), CONCAT("updated row row with id ", OLD.id, " old f1:", OLD.f1, " new f1: ", NEW.f1 ));
--new-table-name
--force
--tries
例子:
--tries create_triggers:5:0.5,drop_triggers:5:0.5
格式:
operation:tries:wait[,operation:tries:wait]
注意:大多数操作只在 MySQL 5.5 和更新版本中受到 lock_wait_timeout(参见 --set-vars)的影响,因为元数据锁。
对于创建和删除触发器,尝试的次数应用于每个触发器的 create trigger 和 drop trigger 语句。
对于复制行,尝试的次数适用于每个块,不是整个 table。
对于交换表,尝试的次数通常只应用一次,因为通常只有一个 rename table 语句。
对于重新构建外键约束,每个语句都有相应的尝试次数(用于重新构建约束的 alter 语句:--alter-foreign-keys-method;drop_swap 方法的其他语句)
下面这些错误出现时,将会重试,
Lock wait timeout (innodb_lock_wait_timeout and lock_wait_timeout)
Deadlock foundQuery is killed (KILL QUERY <thread_id>)
Connection is killed (KILL CONNECTION <thread_id>)
Lost connection to MySQL
错误和重新尝试次数被记录在 --statistics 中。
④ 负载相关
--critical-load
--max-flow-ctl
--max-load
--sleep
⑤ 配置类
--charset "string", -A
--default-engine
--defaults-file, -F
--data-dir
--remove-data-dir
--set-vars
wait_timeout=10000
innodb_lock_wait_timeout=1
lock_wait_timeout=60
--config
--pause-file "string"
Sleeping 60 seconds because /tmp/a.txt exists
⑥ 复制 chunk 类
--chunk-size
--chunk-time
--chunk-size-limit
--chunk-index
--chunk-index-columns
⑦ slave 相关
--slave-user
--slave-password
--channel
--max-lag
--recurse
--recursion-method
⑧ check 类
--check-interval
--check-slave-lag
--skip-check-slave-lag
–skip-check-slave-lag h=127.0.0.1,P=12345 –skip-check-slave-lag h=127.0.0.1,P=12346
--[no]check-replication-filters
--[no]check-alter
--[no]check-plan
--[no]check-unique-key-change
--[no]version-check