参考文章:
http://www.mamicode.com/info-detail-590165.html
http://www.cnblogs.com/zhoujinyi/archive/2013/05/09/3067045.html
http://blog.chinaunix.net/uid-16844903-id-3360228.html
pt-table-checksum会智能分析表上的索引,然后把表中的数据splite分成若干chunk,计算的时候以chunk为单位。
对于每一个chunk,把chunk中的每行每列的值都转换为字符串,然后用concat_ws()函数把转换后的字符串都连接起来,计算出该行的checksum值。checksum默认采用crc32计算。
演示如下:(只用作解释原理,与后文无关。)
mysql> insert into test(name) values ('chaoyangqu');
mysql> select * from test; +----+------------+
| id | name | +----+------------+
| 4 | chaoyangqu | +----+------------+
4 rows in set (0.00 sec)
mysql> select concat_ws(',',id,name) from test; +------------------------+
| concat_ws(',',id,name) | +------------------------+
| 4,chaoyangqu | +------------------------+
4 rows in set (0.00 sec)
mysql> select crc32(concat_ws(',',id,name)) from test; +-------------------------------+
| crc32(concat_ws(',',id,name)) | +-------------------------------+
| 1425884295 | +-------------------------------+
4 rows in set (0.00 sec)
crc32函数生成的校验码,会被插入percona库的checksums表中。
因为是主从环境,所以checksums表中的数据也会被复制到slave中。
也就是slave的percona.checksums表中存储的是主库数据的校验码。
所以在slave中对数据执行同样的校验,然后对比checksums表中的数据,就可验证主从是否一致。
所以执行pt-table-checksum命令的帐号,至少需要有全库的只读权限和percona库的读写权限。
如果发生不一致,可以使用pt-table-sync命令修复。
主从数据校验的过程,pt-table-sync与pt-table-checksum的算法和原理一样。
再往下,就开始有所不同:
pt-table-checksum只是校验,所以它把checksum结果存储到统计表,然后把执行过的sql语句记录到binlog中,任务就算完成。语句级的复制把计算逻辑传递到从库,并在从库执行相同的计算。pt-table-checksum的算法本身并不在意从库的延迟,延迟多少都一样计算,不会影响计算结果的正确性(但是我们还是会检测延迟,因为延迟太多会影响业务,所以总是要加上—max-lag来限流)。
pt-table-sync则不同。它首先要完成chunk的checksum值的计算,一旦发现主从上同样的chunk的checksum值不同,就深入到该chunk内部,逐行比较并修复有问题的行。其计算逻辑描述如下(以修复主从结构的数据不一致为例,业务双写的情况修复起来更复杂—因为涉及到冲突解决和基准选择的问题。):
对每一个从库,每一个表,循环进行如下校验和修复过程。对每一个chunk,在校验时加上for update锁。一旦获得锁,就记录下当前主库的show master status值。
在从库上执行select master_pos_wait()函数,等待从库sql线程执行到show master status得到的位置。以此保证,主从上关于这个chunk的内容均不再改变。
对这个chunk执行checksum,然后与主库的checksum进行比较。
如果checksum相同,说明主从数据一致,就继续下一个chunk。
如果checksum不同,说明该chunk有不一致。深入chunk内部,逐行计算checksum并比较(单行的checksum的比较过程与chunk的比较过程一样,单行实际是chunk的size为1的特例)。
如果发现某行不一致,则标记下来。继续检测剩余行,直到这个chunk结束。
对找到的主从不一致的行,采用replace into语句,在主库执行一遍以生成该行全量的binlog,并同步到从库,这会以主库数据为基准来修复从库;对于主库有的行而从库没有的行,采用replace在主库上插入(必须不能是insert);对于从库有而主库没有的行,通过在主库执行delete来删除(pt-table-sync强烈建议所有的数据修复都只在主库进行,而不建议直接修改从库数据;但是也有特例。)。
直到修复该chunk所有不一致的行。继续检查和修复下一个chunk。
直到这个从库上所有的表修复结束。开始修复下一个从库。
pt-table-checksum是一个在线验证主从数据一致性的工具,主要用于以下场景:
1. 数据迁移前后,进行数据一致性检查
2. 当主从复制出现问题,待修复完成后,对主从数据进行一致性检查
3. 把从库当成主库,进行数据更新,产生了”脏数据”
4. 定期校验
[root@mysqlrep1 tmp]# export LANG=C
[root@mysqlrep1 tmp]# wget https://www.percona.com/downloads/percona-toolkit/2.2.16/RPM/percona-toolkit-2.2.16-1.noarch.rpm
[root@mysqlrep1 tmp]# ll
total 307388
-rw-r--r-- 1 root root 313028660 Feb 16 20:08 mysql-5.6.29-linux-glibc2.5-x86_64.tar.gz
-rw-r--r-- 1 root root 1714826 Nov 9 19:20 percona-toolkit-2.2.16-1.noarch.rpm
-rw-------. 1 root root 0 Feb 16 07:51 yum.log
-rw-rw-r-- 1 zabbix zabbix 13045 Feb 16 22:10 zabbix_agentd.log
[root@mysqlrep1 tmp]#
[root@mysqlrep1 tmp]# yum localinstall percona-toolkit-2.2.16-1.noarch.rpm
主库:
mysql> create database ljk;
mysql> use ljk;
Database changed
mysql> create table test(id serial,name varchar(20));
Query OK, 0 rows affected (0.06 sec)
mysql> insert into test(name) values ('hello'),('beijing'),('shandong');
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from test; +----+----------+
| id | name | +----+----------+
| 1 | hello |
| 2 | beijing |
| 3 | shandong | +----+----------+
3 rows in set (0.00 sec)
从库:
mysql> use ljk;
Database changed
mysql> select * from test; +----+----------+
| id | name | +----+----------+
| 1 | hello |
| 2 | beijing |
| 3 | shandong | +----+----------+
3 rows in set (0.00 sec)
mysql> insert into test(name) values ('chaoyangqu');
Query OK, 1 row affected (0.00 sec)
建立主从校验所需账户
mysql> GRANT SELECT, PROCESS, SUPER, REPLICATION SLAVE ON *.* TO 'lijingkuan'@'%' IDENTIFIED BY 'lijingkuan';
Query OK, 0 rows affected (0.00 sec)
mysql> grant all privileges on percona.* TO 'lijingkuan'@'%' IDENTIFIED BY 'lijingkuan';
Query OK, 0 rows affected (0.00 sec)
执行主从校验
[root@mysqlrep1 tmp]# pt-table-checksum --user=lijingkuan --password=lijingkuan --recursion-method=processlist --databases=ljk --no-check-binlog-format TS ERRORS DIFFS ROWS CHUNKS SKIPPED TIME TABLE 02-24T01:05:57 0 1 3 1 0 0.289 ljk.test
[root@mysqlrep1 tmp]#
主从校验结果
(自动创建了percona库和checksums表)
mysql> show databases; +--------------------+
| Database | +--------------------+
| information_schema | | ljk | | mysql | | percona | | performance_schema |
| test | +--------------------+
6 rows in set (0.00 sec)
mysql> use percona;
mysql> show tables; +-------------------+
| Tables_in_percona | +-------------------+
| checksums | +-------------------+
1 row in set (0.00 sec)
mysql> select * from checksums; +-----+------+-------+------------+-------------+----------------+----------------+----------+----------+------------+------------+---------------------+
| db | tbl | chunk | chunk_time | chunk_index | lower_boundary | upper_boundary | this_crc | this_cnt | master_crc | master_cnt | ts | +-----+------+-------+------------+-------------+----------------+----------------+----------+----------+------------+------------+---------------------+
| ljk | test | 1 | 0.027644 | NULL | NULL | NULL | 7a1300c8 | 3 | 7a1300c8 | 3 | 2016-02-24 01:05:57 | +-----+------+-------+------------+-------------+----------------+----------------+----------+----------+------------+------------+---------------------+
1 row in set (0.00 sec)
mysql> exit
Bye
查看需要执行的SQL语句
使用print参数,他会在屏幕显示修复的SQL语句。然后可以手工确认并执行。
[root@mysqlrep1 tmp]# pt-table-sync --print --replicate=percona.checksums h=192.168.56.211,u=root,p=123456,P=3306 h=192.168.56.212,u=root,p=123456,P=3306
DELETE FROM `ljk`.`test` WHERE `id`='4' LIMIT 1 /*percona-toolkit src_db:ljk src_tbl:test src_dsn:P=3306,h=192.168.56.211,p=...,u=root dst_db:ljk dst_tbl:test dst_dsn:P=3306,h=192.168.56.212,p=...,u=root lock:1 transaction:1 changing_src:percona.checksums replicate:percona.checksums bidirectional:0 pid:4795 user:root host:mysqlrep1.cwlc.com.cn*/;
[root@mysqlrep1 tmp]#
[root@mysqlrep1 tmp]#
直接执行修复
[root@mysqlrep1 tmp]# pt-table-sync --execute --sync-to-master h=192.168.56.212,u=root,p=123456,P=3306 --databases ljk --tables test
[root@mysqlrep1 tmp]#
–replicate= :把checksum的信息写入到指定表中,建议直接写到被检查的数据库当中。
举例:–replicate=test.checksums 将校验数据写入test数据库的checksums表中。
–databases= :指定需要被检查的数据库,多个则用逗号隔开。
–tables= :指定需要被检查的表,多个用逗号隔开
–chunk-size, –chunk-size-limit 用于指定检测块的大小。 可控性更强。
–lock-wait-timeout innodb 锁的超时设定, 默认为1。
–max-load 设置最大并发连接数。
–ask-pass 要是在shell窗口不想显示的输入密码则可以添加–ask-pass 参数。
Diffs cannot be detected because no slaves were found. Please read the --recursion-method documentation for information.
原因:
pt-table-checksum默认查找备库的方式是使用SHOW PROCESSLIST。
如果非标准的3306端口,就使用SHOW SLAVE HOSTS的方式,推荐使用dsn方式,手动指定。
所以执行pt-table-checksum的数据库账户要有process和replication slave权限。
解决方案:
加上参数 –recursion-method=processlist
Replica mysqlrep2.cwlc.com.cn has binlog_format ROW which could cause pt-table-checksum to break replication. Please read "Replicas using row-based replication" in the LIMITATIONS section of the tool's documentation. If you understand the risks, specify --no-check-binlog-format to disable this check.
原因及解决办法:
pt-table-checksum requires statement-based replication, and it sets binlog_format=STATEMENT on the master, but due to a MySQL limitation replicas do not honor this change. Therefore, checksums will not replicate past any replicas using row-based replication that are masters for further replicas.
The tool automatically checks the binlog_format on all servers. See –[no]check-binlog-format .
(Bug 899415)
1.要是表中没有唯一索引或则主键则会报错:
Can’t make changes on the master because no unique index exists at /usr/local/bin/pt-table-sync line 10591.
2.要是从库有的数据,而主库没有,那这个数据怎么处理?会给出删除SLAVE多余数据,和修复SLAVE缺失数据的SQL语句。