top 10 percona toolkit tools(五)

9. pt-table-checksum

pt-table-checksum.html 工具用来对主从表数据进行一致校验: 该工具通过分组(chunk)方式以hash, md5, cac32或自定义函数生成每个分组数据的检验串, 分别在master和slave端执行, 如果每个分组的校验串一致, 则认为该分组的数据在master和slave一致。详见: mysql 主从一致性校验, 这种方式可以相对有效的找出主从中哪个chunk组的数据不一致, 进而再继续细分chunk, 找出具体的行。 不过分组校验不一定能够严格校验主从的不一致, 这依赖校验函数的冲突率有多大, 默认的crc32函数的冲突率还是偏大的, 如果恰好有几个字符串算出的结果一样, 则该工具出现漏报的可能性, 误报的可能性不能完全杜绝。

该工具由以下限制:

(1) 校验数据是假设主从的schema和table结构在master和slave上一致.
(2) 主从复制格式需要为statement格式, 该工具默认检测binlog_format参数, 如果想忽略检测可以指定 --nocheck-binlog-format

该工具默认情况下创建percona.checksums表用于保存校验的结果, 该表有2个作用: (1) 方面查看哪些信息不一致, 可以使用以下语句:

SELECT db, tbl, SUM(this_cnt) AS total_rows, COUNT(*) AS chunks FROM checksums WHERE 
  ( master_cnt <> this_cnt OR master_crc <> this_crc OR ISNULL(master_crc) <> ISNULL(this_crc)) GROUP BY db, tbl;

(2) 方便pt-table-sync(数据纠错)的增量使用.

举例如下:

[root@cz ~]# pt-table-checksum h=10.3.254.110,u=root,p=xxxxxx,P=30587 --databases="part1" --tables="book" --nocheck-replication-filters 
            TS ERRORS  DIFFS     ROWS  CHUNKS SKIPPED    TIME TABLE
04-11T12:09:46      0      1   168949       4       0   1.218 part1.book

diffs列显示主从约有1个分组的记录不一致, book表一共检测了168949行记录, 分4次校验完. 参数信息也可以写到文件里,通过–config参数指定,比如对test表进行校验, 将参数写到pt-table.cnf文件中:

host           = 127.0.0.1
user           = root
password       = xxxxxxxx
port           = 3306
databases      = test
tables         = test
chunk-size     = 3000
max-lag        = 5
check-interval = 3
recursion-method=processlist
recurse        = 1
#resume
#replicate-check-only

pt-table-checksum --config pt-table.cnf 即可开始校验test.test表, 不指定tables参数, 则检测test库的所有表;

其它参数:

--[no]check-binlog-format: 默认情况下该工具检测所有server的binlog_format, 该参数用来控制是否检测.

--check-interval: 如果指定了 --max-log参数, 则每次检测的时候sleep指定的时间,默认为1s.

--[no]check-plain: 出于安全方面的因素,需要检测query的查询执行计划(EXPLAIN), 默认为yes.

--[no]check-replication-filters:出于一些原因slave可能会配置binlog_ignore_db或replicate_do_db这些参数, 默认情况下该工具检测到有过滤规则则返回error并推出.

--check-slave-lag: 如果指定了max-lag参数,并且slave延迟的值大于max-lag则暂停校验, 待恢复后继续执行.

--chunk-index: 默认情况下该工具选择最有可能的索引进行chunk, 该参数用来指定索引进行chunk分组.

--columns: 用于指定只检测表的指定列的信息.

--explain: 只显示却不执行校验的语句.

--function: 用于指定校验数据时用的函数, 默认为crc32, 可以是FNV1A_64, MURMUR_HASH, SHA1, MD5, CRC32等.

--resume: 从上一次校验完成后的chunk开始执行本次的校验, 该参数可用于增量校验大表数据. 如果应用有很多update或delete操作, 则不应该启用该参数.

10. pt-table-sync

pt-table-sync 数据同步工具: 该工具主要用不同MySQL的表之间的数据同步, 可以是master->slave, master->master或一个instance到另一个instance. 同步功能会做一些数据的修改操作, 如果表信息很重要, 操作前可以备份好相关的表. 这里主要介绍指定replicate或sync-to-master参数时, pt-table-sync工具如何工作. 以下为该工具的处理逻辑:

if DSN has a t part, sync only that table:
   if 1 DSN:
      if --sync-to-master:
         The DSN is a slave.  Connect to its master and sync.
   if more than 1 DSN:
      The first DSN is the source.  Sync each DSN in turn.
else if --replicate:
   if --sync-to-master:
      The DSN is a slave.  Connect to its master, find records
      of differences, and fix.
   else:
      The DSN is the master.  Find slaves and connect to each,
      find records of differences, and fix.
else:
   if only 1 DSN and --sync-to-master:
      The DSN is a slave.  Connect to its master, find tables and
      filter with --databases etc, and sync each table to the master.
   else:
      find tables, filtering with --databases etc, and sync each
      DSN to the first.

按照手册页的例子来说明:

(1) pt-table-sync --execute --sync-to-master slave1

如果指定了sync-to-master选项, DSN应该为slave的连接信息, 这时pt-table-sync会连接该slave的master, 并将master的数据同步到该slave.

(2) pt-table-sync --execute --replicate test.checksum master1

replicate选项用到了上一个工具pt-table-checksum, 其读取pt-table-checksum生成的checksum表信息, 然后进行同步操作, master1表示同步master1的数据到所有的slave节点.

(3) pt-table-sync --execute --replicate test.checksum --sync-to-master slave1

同上面的命令, 但是只同步master1的数据到slave1节点. 在同步方面, 安全的方式是在master上更新数据(比如,delete或replace等), 通过主从复制来完成同步, 这种方式和其它正常的更新一样,通过主从机制完成. 同步的过程: 同步过程主要包括以下操作:

(1) 如果没有指定replicate选项, 则先对数据进行分组(在master和slave端)校验, 校验的方式同pt-table-checksum, 找到不同的chunk组之后, 
    再对该组进行细分进行校验直到找到相关的行记录; 找到记录后, 在master中进行数据的更改, 通过主从复制来实现数据的同步.

(2) 如果指定了replicate选项, 则读取checksums表, 该步骤免去了校验过程, 可以得到不同的chunk组信息, 后续的过程同(1)的操作.

pt-table-sync的校验过程举例如下:

[root@cz rsandbox_Percona-Server-5_6_15]# pt-table-sync --databases percona --execute --sync-to-master  h=localhost,P=19681,u=root,p=xxxxxx,S=/tmp/mysql_sandbox19681.sock --verbose --print --recursion-method=hosts --chunk-size=1
# Syncing P=19681,S=/tmp/mysql_sandbox19681.sock,h=localhost,p=...,u=root
# DELETE REPLACE INSERT UPDATE ALGORITHM START    END      EXIT DATABASE.TABLE
REPLACE INTO `percona`.`dept`(`id`, `name`) VALUES ('30', 'insert test') /*percona-toolkit src_db:percona src_tbl:dept src_dsn:P=19680,S=/tmp/mysql_sandbox19681.sock,h=127.0.0.1,p=...,u=root dst_db:percona dst_tbl:dept dst_dsn:P=19681,S=/tmp/mysql_sandbox19681.sock,h=localhost,p=...,u=root lock:1 transaction:1 changing_src:1 replicate:0 bidirectional:0 pid:9674 user:root host:z10*/;
#      0       1      0      0 Chunk     15:37:33 15:37:33 2    percona.dept

上述信息描述了同步percona.dept表的信息过程, master比slave多了一条记录(‘30’, ‘insert test’), pt-table-checksum检测到后通过在master上使用replace这条sql实现了两边的同步, 打开master或slave的general log参数, 可以看到以下信息:

                   67 Query     START TRANSACTION /*!40108 WITH CONSISTENT SNAPSHOT */
                   67 Query     SELECT /*percona.dept:18/20*/ 17 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`id`), ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `percona`.`dept` FORCE INDEX (`id_idx`) WHERE (`id` >= '28' AND `id` < '29') FOR UPDATE
                   68 Query     SHOW MASTER STATUS
                   67 Query     SET @crc := '', @cnt := 0
                   67 Query     commit
                   67 Query     START TRANSACTION /*!40108 WITH CONSISTENT SNAPSHOT */
                   67 Query     SELECT /*percona.dept:19/20*/ 18 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`id`), ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `percona`.`dept` FORCE INDEX (`id_idx`) WHERE (`id` >= '29') FOR UPDATE
                   68 Query     SHOW MASTER STATUS
                   67 Query     SET @crc := '', @cnt := 0
                   67 Query     SELECT /*rows in chunk*/ `id`, `name`, CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`id`), ISNULL(`name`)))) AS __crc FROM `percona`.`dept` FORCE INDEX (`id_idx`) WHERE (`id` >= '29') ORDER BY `id` FOR UPDATE
                   67 Query     SELECT `id`, `name` FROM `percona`.`dept` WHERE `id`='30' LIMIT 1
                   67 Query     REPLACE INTO `percona`.`dept`(`id`, `name`) VALUES ('30', 'insert test') /*percona-toolkit src_db:percona src_tbl:dept src_dsn:P=19680,S=/tmp/mysql_sandbox19681.sock,h=127.0.0.1,p=...,u=root dst_db:percona dst_tbl:dept dst_dsn:P=19681,S=/tmp/mysql_sandbox19681.sock,h=localhost,p=...,u=root lock:1 transaction:1 changing_src:1 replicate:0 bidirectional:0 pid:9674 user:root host:cz*/
                   67 Query     SET @crc := '', @cnt := 0
                   67 Query     commit
                   67 Query     START TRANSACTION /*!40108 WITH CONSISTENT SNAPSHOT */
                   67 Query     SELECT /*percona.dept:20/20*/ 19 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`id`), ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `percona`.`dept` FORCE INDEX (`id_idx`) WHERE (`id` IS NULL) FOR UPDATE

可以看到校验的过程和pt-table-checksum是类似的, 通过chunk(chunk-size=1)的方式分组校验master和slave的数据, 上述信息的where id >= ? 信息可以看到此变化, 找到id=30的记录后(slave没有id=30的记录), 在master上执行replace语句, 通过主从复制保证了两边表数据的最终一致.

其它参数:

--algorithms:使用什么算法来发现两边数据的不同, 默认为chunk, 详细可以参见手册页algorithms部分.

--[no]bin-log: pt-table-sync在执行更新操作的时候(如上面的replace语句), 是否需要记录到binlog日志里, 默认为记录.

--[no]check-child-tables: 如果待同步的表存在外键约束, 比如 ON DELETE CASCADE, ON UPDATE CASCADE, 该工具会打印错误并退出, 该参数用户指定是否需要检测外键约束.

--[no]check-slave: 是否检测目的Server是一个slave.

--[no]check-triggers: 检测表没有trigger信息.

--columns: 只校验指定列的信息.

--[no]foreign-key-checks: 是否检测外键, 默认为检测.

--function: 用于指定校验的函数, 同pt-table-checksum工具的该选项.

--[no]hex-blob: 如果表字段有二进制(blob,binary等)字段信息, 则用hex()函数进行封装处理,以避免产生一个无效的sql语句.

--lock: 在校验数据的时候是否进行锁表操作.

--print: 打印更新操作的sql语句信息.