The Flow Of Percona Toolkit pt-table-checksum
原创
©著作权归作者所有:来自51CTO博客作者陈振阳Plus的原创作品,请联系作者获取转载授权,否则将追究法律责任
chunk 算法
PT-TABLE-CHECKSUM 首先对要比对的表根据 index 信息分块(chunk),分块的逻辑使用了 Percona Toolkit 中被称为 nibbling 的算法,此算法在 Percona Toolkit 的很多组件比如 pt-archiver,pt-table-sync 等组件中都有使用,下回分解;
checksums 表结构
PT-TABLE-CHECKSUM 连接一个 MySQL 集群的 master 节点, 在 master 节点上创建如下结构的数据表,注意 Primary Key 为 database + table + chunk,也就是每个数据表的每个分块在 checksums 表里都会有且只有一条记录;此表在 master 节点上创建,通过 MySQL binlog 协议在每个连接此 master 节点的 slave 节点上都会创建:
CREATE TABLE checksums (
db CHAR(64) NOT NULL,
tbl CHAR(64) NOT NULL,
chunk INT NOT NULL,
chunk_time FLOAT NULL,
chunk_index VARCHAR(200) NULL,
lower_boundary TEXT NULL,
upper_boundary TEXT NULL,
this_crc CHAR(40) NOT NULL,
this_cnt INT NOT NULL,
master_crc CHAR(40) NULL,
master_cnt INT NULL,
ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (db, tbl, chunk),
INDEX ts_db_tbl (ts, db, tbl)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
db:数据库
tbl:数据表
chunk:序列号
chunk_time:执行时间?
chunk_index:
lower_boundary + upper_boundary :当前 chunk 的上下限条件信息;
this_crc:在当前数据库服务器上的当前数据表的当前 chunk 中所有记录的 crc 计算结果;
this_cnt:在当前数据库服务器上的当前数据表的当前 chunk 中所有记录的条数;
master_crc:master 节点数据库服务器上的当前数据表的当前 chunk 中所有记录的 crc 计算结果;
master_cnt:master 节点数据库服务器上的当前数据表的当前 chunk 中所有记录的条数;
ts:本条记录的时间戳,记录有变化就会更新;
执行流程
如下是 PT-TABLE-CHECKSUM 的执行结果输出:
root@a4ea42f908f1:/# pt-table-checksum -h 127.0.0.1 --user root --password master_root_password --databases my_database
Checking if all tables can be checksummed ...
Starting checksum ...
TS ERRORS DIFFS ROWS DIFF_ROWS CHUNKS SKIPPED TIME TABLE
07-05T07:07:20 0 1 2 0 1 0 0.403 my_database.test
如果此 master 有多个 slave ,则执行结果会生成多个类似上图的表格,每个 slave 一个。
通过分析 MySQL binlog 日志,PT-TABLE-CHECKSUM DEBUG 信息和 PT-TABLE-CHECKSUM 源码整理 PT-TABLE-CHECKSUM 的核心工作流程如下:
在 master 节点上计算 this_crc 和 this_cnt,并将结果更新到 master 节点的 checksums 表;
# pt_table_checksum:11780 506 REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `my_database`.`test` /*checksum table*/ lower boundary: upper boundary:
所有连接此 master 节点的 slave 节点通过 binlog 协议接收到 replace select SQL,然后在本地执行 replace select 更新本地的 checksums (this_crc,this_cnt);
此时整个 MySQL 集群所有服务器上的 checksum 表还剩 master_crc 和 master_cnt 没有值。PT-TABLE-CHECKSUM 连接 master 节点执行类似如下的SQL查询当前正在处理的数据表 chunk 的 this_crc 和 this_cnt 信息,然后更新到此及记录的 master_crc 和 this_cnt(master 节点的 this 也就是 master):
SELECT this_crc, this_cnt FROM $repl_table WHERE db = ? AND tbl = ? AND chunk = ?
UPDATE `percona`.`checksums` SET chunk_time = '0.027939', master_crc = '9b4fefe3', master_cnt = '2' WHERE db = 'my_database' AND tbl = 'test' AND chunk = '1' ;
在 master 节点上执行的 UPDATE 语句,通过 MySQL binlog 协议在所有的 slave 节点原样执行,更新 slave 节点本地的 checksums 表上的记录的 master_crc 和 master_cnt 信息;
查询每个 slave 上的 checksums 表整合输出结果;
SELECT CONCAT(db, '.', tbl) AS `table`, chunk, chunk_index, lower_boundary, upper_boundary, COALESCE(this_cnt-master_cnt, 0) AS cnt_diff, COALESCE(this_crc <> master_crc OR ISNULL(master_crc) <> ISNULL(this_crc), 0) AS crc_diff, this_cnt, master_cnt, this_crc, master_crc FROM `percona`.`checksums` WHERE (master_cnt <> this_cnt OR master_crc <> this_crc OR ISNULL(master_crc) <> ISNULL(this_crc)) AND (db='my_database' AND tbl='test')