Mysql的复制是通过传输binlog来实现,是一种逻辑复制,然而这种复制方式也带来了主从不一致的风险。因此主从不一致的校验对一个DBA来说是至关重要的。perconapt-table-checksum一直作为主从不一致校验的热门工具,让我们现在来一起了解他吧。

pt-table-checksum通过sql在主库执行数据块的校验,然后把相同的语句传送到从库,并在从库上计算数据块的校验,最后将主从库相同块的校验值进行对比,辨别主从不一致。

 

一、事例

pt-table-checksum --nocheck-replication-filters--no-check-binlog-format --replicate=test.checksum --databases=freedomh=127.0.0.1,u=root,P=3301  --tables=a1--chunk_size=100

事例中,本机用root账户登录登录端口为3301的数据库,对freedom数据库的a1表进行主从一致检查,每次检查以100行数据为一个块。

装完pt工具后,运行其中的pt-table-checksum

nocheck-replication-filters:不检查复制过滤器,建议启用。后面可以用--databases来指定需要检查的数据库。

--no-check-binlog-format : 不检查复制的binlog模式,要是binlog模式是ROW

--replicate校验表

--databases –tables 所需检查的数据库名和表名

--chunk_size 块大小定义(即一个块包含的行数)

pt-table-checksum –help 查看还有其他安全参数,限速选项(避免主从延迟太大),过滤选项(可以指定表或数据库)

 

二、内部工作重要过程

  1. 1.    SET @@binlog_format := 'STATEMENT'

  2. 创建校验信息存放表

  3. SET SESSION TRANSACTIONISOLATION LEVEL REPEATABLE READ

  4. 查找表的索引列

  5. 确定分块的第一行,和分块的最后行索引数值

  6. 查出该块的所有记录,并且做校验,最后插入检验表

  7. 查出主库校验值和校验行数更新到校验信息表的master_crc, master_cnt列,执行下一个块回到步骤5

  8. 通过sql命令查出校验表中主从校验不一致的信息。

 

 

详解:

  1. pt-table-checksum通过相同sql在主从库进行计算而校验出来的,所以需要将binlog格式需改成STATEMENT

  2. 3.修改隔离级别,通过加锁保证在取数据行数(replaceinto select  from )到计算出检验值这段时间数据不会被修改。他采用块,来减少每次锁住数据的行数,这样提高了并发性。

  3. 查出分块区间。首先判断是否为第一个块,如果是的话查找第一个索引值,然后根据第一个索引值查出该快最后个索引值。如果不是第一个块,根据上个块的最后个索引值作为标准,查出该块的第一个索引和最后个索引。

1个块:

SELECT id` FROM `freedom`.`a1` FORCE INDEX(`PRIMARY`) WHERE `id` IS NOT NULL ORDER BY `id` LIMIT1

先根据表a1的主键索引排序,然后得到第一个索引值。

SELECT /*!40001 SQL_NO_CACHE */ `id` FROM`freedom`.`a1` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1')) ORDER BY `id`LIMIT 100, 2

因为块数大小为100,查出该块中最后个索引值的大小。

2个块:

SELECT /*!40001 SQL_NO_CACHE */ `id` FROM`freedom`.`a1` FORCE INDEX(`PRIMARY`) WHERE ((`id` > '100')) ORDER BY `id`LIMIT 101, 2

上个块的最后个索引值为100,查出其后的100个索引值,得到该快的第一个索引和最后个索引。

6.计算校验值

wKioL1aTT0HRlJSCAAAi8G2gBYs178.png

先根据该块的索引区间,将该块所有行数据进行合并然后做校验,然后插入检验表。插入检验表的信息有,数据库名,表名,块号,第一个索引,最后个索引,块的行数以及块的内容。

7. 将主库的校验值更新到主从校验表。

UPDATE `test`.`checksum` SET chunk_time ='0.002492', master_crc = 'd7ac8fcb', master_cnt = '100' WHERE db = 'freedom'AND tbl = 'a1' AND chunk = '1'

将主库的校验值更新到主从校验表。

9. 在从库检查主从不同步。

select * from test.checksums where master_cnt <>this_cnt OR master_crc <> this_crc OR ISNULL(master_crc) <>ISNULL(this_crc) \G

查出检验位或行数不同的数据


三、性能影响

数据库层面:并发性上,通过块来大大减少锁的影响,从而又能保证数据一致性又能保证检验数据的准确性。主从延迟问题上,因为主库更新可以并发,然而从库Slave_SQL_Running线程是单线程这种速率差异将会导致一个时间段中主从数据不一致,这样将导致检验数据不准确,主库执行sql检验语句再通过复制原理最后从库再执行sql语句,虽然主从执行校验的时间不同但保证了数据的一致性。主从不一致过大,也可以通过限速选项来避免。

   服务器层面:从图表可以看到因为大量计算校验值,cpu消耗量较大。可以通过减少块数的大小来减轻cpu的负载,但需要增加处理时间IO,每个块需要读操作做一次以及写操作两次,对于IO的负载是比较低的。

                       1G数据 200W条数据量
   块数大小      CPU(%)   IO(%)   处理时间(分钟)
   1000 45
   7
    1
    100  28   7    4


四、注意事项

1.从库配置表上需要加上,

report_host=slave_ip
report_port=slave_port

不然将报Diffscannot be detected because no slaves were found.错误

2.表中没有索引,pt-table-checksum将没办法处理

3.表中只有普通索引。当数据列有重复并且正好在分块的交界中,将会报错。所以表中最好有一个唯一索引列

01-07T12:05:29 Error checksumming tablefreedom.a6: Possible infinite loop detected! The lower boundary for chunk 2 is <cc, cc> and the lower boundaryfor chunk 3 is also <cc, cc>.  Thisusually happens when using a non-unique single column index.  The current chunk index for table freedom.a6is name which is not unique and covers 1 colum

4.块的定义过大。当块大于表的行数时,将会产生全表锁。