pt-table-checksum是percona公司提供的一个用于在线比对主从数据一致性的工具。

实现原理

将一张大表分成多个chunk,每次针对一个chunk进行校验,同时将校验的结果通过REPLACE INTO语句写入到percona.checksums表中,然后该语句通过主从复制,在SLAVE中同样执行一次,校验的结果同样是写入到percona.checksums表中,最后,通过查询percona.checksums来获取主从不一致的信息。

常见用法

1. 基本用法

# pt-table-checksum -h192.168.244.10 -umonitor -pmonitor123

其中,monitor的最小权限如下(第二个权限是针对percona.checksums的):

GRANT SELECT, PROCESS, SUPER, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'monitor'@'192.168.244.10';

GRANT ALL PRIVILEGES ON `percona`.* TO 'monitor'@'192.168.244.10';

2. pt-table-checksum默认是运行在statement下,如果是其它日志格式,需加--no-check-binlog-format参数

# pt-table-checksum -h192.168.244.10 -umonitor -pmonitor123 --no-check-binlog-format

3. 如果主从复制中加了过滤条件,譬如binlog_ignore_db或replicate_do_db之类的参数,需加--no-check-replication-filters参数

# pt-table-checksum -h192.168.244.10 -umonitor -pmonitor123 --no-check-binlog-format --no-check-replication-filters

如果在对被过滤表进行校验时,命令hang住了,可加--replicate-database参数。

4. 基于指定库的校验

# pt-table-checksum -h192.168.244.10 -umonitor -pmonitor123 --no-check-binlog-format --databases=test,test1

5. 基于指定表的校验

# pt-table-checksum -h192.168.244.10 -umonitor -pmonitor123 --no-check-binlog-format --tables=test2.test

其它具体用法,可参考另外一篇博客:pt-table-checksum参数详解

通过打开general_log来看看其具体的执行过程,注意,测试表是test.test,共1000000条记录。

 Query    SHOW GLOBAL STATUS LIKE 'Threads_running'
查看Threads_running变量是为了查看当前系统的负载情况
Query /*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := '', @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */
设置会话变量
Query USE `test`
Query SHOW CREATE TABLE `test`.`test`
查看test表的表结构,选取分片键,一般为主键或唯一索引
Query /*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, @@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */
Query EXPLAIN SELECT * FROM `test`.`test` WHERE =1
查看test表的大概数量
Query SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`test` FORCE INDEX(`PRIMARY`) ORDER BY `id` LIMIT /*first lower boundary*/
选择第一个chunk的下标,即id的最小值
Query SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`test` FORCE INDEX (`PRIMARY`) WHERE `id` IS NOT NULL ORDER BY `id` LIMIT /*key_len*/
查看索引的长度
Query EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * FROM `test`.`test` FORCE INDEX (`PRIMARY`) WHERE `id` >= '' /*key_len*/
查看实际使用的索引的长度,这个针对联合索引的场景。
Query USE `percona`
Query DELETE FROM `percona`.`checksums` WHERE db = 'test' AND tbl = 'test'
从percona.checksums表中删除之前的校验记录
Query USE `test`
Query EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) ORDER BY `id` LIMIT , /*next chunk boundary*/
17 Query SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) ORDER BY `id` LIMIT , /*next chunk boundary*/
确认本chunk的上限,以及下一个chunk的下限。
Query EXPLAIN SELECT COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `ip`, `no`, CONCAT(ISNULL(`ip`), ISNULL(`no`)))) AS UNSIGNED)), , )), ) AS crc FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) AND ((`id` <= '')) /*explain checksum chunk*/
查看对本次chunk执行checksum操作的执行计划,确认读取的行数是否合理,选择的索引是否合适
17 Query REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'test', 'test', '', 'PRIMARY', '', '', COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `ip`, `no`, CONCAT(ISNULL(`ip`), ISNULL(`no`)))) AS UNSIGNED)), , )), ) AS crc FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) AND ((`id` <= '')) /*checksum chunk*/
关键操作,对本次chunk执行checksum操作,并将结果更新到percona.checksums表中。
Query SHOW WARNINGS
Query SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'test' AND tbl = 'test' AND chunk = ''
查看本次操作校验的行数和校验和
Query UPDATE `percona`.`checksums` SET chunk_time = '0.059603', master_crc = '568c1ba3', master_cnt = '' WHERE db = 'test' AND tbl = 'test' AND chunk = ''
将上面那个查询得到的行数和校验和更新到master_cnt和master_crc中。这样的话,主库的校验和在从库执行replace操作时被覆盖。 下面是针对第二个chunk执行的操作。
Query SHOW GLOBAL STATUS LIKE 'Threads_running'
Query EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) ORDER BY `id` LIMIT , /*next chunk boundary*/
Query SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) ORDER BY `id` LIMIT , /*next chunk boundary*/
Query EXPLAIN SELECT COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `ip`, `no`, CONCAT(ISNULL(`ip`), ISNULL(`no`)))) AS UNSIGNED)), , )), ) AS crc FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) AND ((`id` <= '')) /*explain checksum chunk*/
Query REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'test', 'test', '', 'PRIMARY', '', '', COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `ip`, `no`, CONCAT(ISNULL(`ip`), ISNULL(`no`)))) AS UNSIGNED)), , )), ) AS crc FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) AND ((`id` <= '')) /*checksum chunk*/
Query SHOW WARNINGS
Query SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'test' AND tbl = 'test' AND chunk = ''
Query UPDATE `percona`.`checksums` SET chunk_time = '0.022960', master_crc = '', master_cnt = '' WHERE db = 'test' AND tbl = 'test' AND chunk = '' ...
下面的校验和上面的并不相同,上述id值的范围是1~1000000,下面两个chunk的范围是<1和>1000000,为什么要这么做呢?
主要是考虑到从库有可能存在上述两个范围的数据。
Query SHOW GLOBAL STATUS LIKE 'Threads_running'
Query EXPLAIN SELECT COUNT(*), '' FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` < '')) ORDER BY `id` /*explain past lower chunk*/
Query REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'test', 'test', '', 'PRIMARY', NULL, '', COUNT(*), '' FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` < '')) ORDER BY `id` /*past lower chunk*/
Query SHOW WARNINGS
Query SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'test' AND tbl = 'test' AND chunk = ''
Query UPDATE `percona`.`checksums` SET chunk_time = '0.004492', master_crc = '', master_cnt = '' WHERE db = 'test' AND tbl = 'test' AND chunk = '' Query SHOW GLOBAL STATUS LIKE 'Threads_running'
Query EXPLAIN SELECT COUNT(*), '' FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` > '')) ORDER BY `id` /*explain past upper chunk*/
Query REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'test', 'test', '', 'PRIMARY', '', NULL, COUNT(*), '' FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` > '')) ORDER BY `id` /*past upper chunk*/
Query SHOW WARNINGS
Query SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'test' AND tbl = 'test' AND chunk = ''
Query UPDATE `percona`.`checksums` SET chunk_time = '0.058622', master_crc = '', master_cnt = '' WHERE db = 'test' AND tbl = 'test' AND chunk = ''

输出结果说明

# pt-table-checksum -h 192.168.244.10 -umonitor -pmonitor123 --no-check-binlog-format

            TS ERRORS  DIFFS     ROWS  CHUNKS SKIPPED    TIME TABLE
-09T21:: 2.768 h2.h3
-09T21:: 3.903 hello.h1
-09T21:: 0.620 hello.h2
-09T21:: 1.725 mysql.columns_priv
-09T21:: 0.457 mysql.db
-09T21:: 0.306 mysql.event
-09T21:: 0.721 mysql.func
-09T21:: 0.649 mysql.help_category
-09T21:: 1.313 mysql.help_keyword
-09T21:: 3.432 mysql.help_relation
-09T21:: 1.632 mysql.help_topic
-09T21:: 0.501 mysql.ndb_binlog_index
-09T21:: 0.319 mysql.plugin
-09T21:: 0.624 mysql.proc
-09T21:: 0.626 mysql.procs_priv
-09T21:: 0.375 mysql.proxies_priv
-09T21:: 0.806 mysql.servers
-09T21:: 0.292 mysql.tables_priv
-09T21:: 0.382 mysql.time_zone
-09T21:: 0.398 mysql.time_zone_leap_second
-09T21:: 0.386 mysql.time_zone_name
-09T21:: 0.393 mysql.time_zone_transition
-09T21:: 0.313 mysql.time_zone_transition_type
-09T21:: 0.393 mysql.user
-09T21:: 0.642 percona.dsns
-09T21:: 2.613 test.checksum
-09T21:: 0.669 test.ta
-09T21:: 42.009 test.test
-09T21:: 1.818 test1.test
-09T21:: 0.546 test1.test1
-09T21:: 0.508 test2.test
-09T21:: 0.387 test2.test2
-09T21:: 0.707 test3.test3

TS:校验完表后的时间戳

ERRORS:校验过程中出现的errors和warnings的次数。

DIFFS:所有SLAVE中checksum值不相同的chunk的数量,如一主两从中,SLAVE1的chunk1与MASTER的checksum不同,SLAVE2的chunk1和chunk2不相同,则DIFFS的值为2。如果SLAVE2的chunk2和chunk3不相同,则DIFFS为3。

ROWS:表中校验的记录数。通常情况下为表的总行数。如果指定了--where选项,则为符合条件的记录数。

CHUNKS:表被分割为多个chunk后,chunk的个数。

SKIPPED:跳过的chunk的个数,通常因为如下原因:

* MySQL not using the --chunk-index
* MySQL not using the full chunk index (--[no]check-plan)
* Chunk size is greater than --chunk-size * --chunk-size-limit
* Lock wait timeout exceeded (--retries)
* Checksum query killed (--retries)

TIME:校验表所花费的时间。

TABLE:校验的表名

replicate-check-only参数下的输出结果说明 

如果指定了--replicate-check-only参数,则意味着不会校验任何表,直接获取上次校验的结果。

# pt-table-checksum -h 192.168.244.10 -umonitor -pmonitor123 --no-check-binlog-format --replicate-check-only

Differences on hbase
TABLE CHUNK CNT_DIFF CRC_DIFF CHUNK_INDEX LOWER_BOUNDARY UPPER_BOUNDARY
mysql.db
mysql.tables_priv -
mysql.user -
test.checksum Differences on test
TABLE CHUNK CNT_DIFF CRC_DIFF CHUNK_INDEX LOWER_BOUNDARY UPPER_BOUNDARY
mysql.db -
mysql.tables_priv -
mysql.user -
test.checksum
test.test PRIMARY
test2.test -
test2.test2
test3.test3

可以看出,它分别输出了不同SLAVE中的差异部分。

TABLE:校验的表名。

CHUNK:checksum值不相同的chunk的数量。

CNT_DIFF:The number of chunk rows on the replica minus the number of chunk rows on the master.即SLAVE中被校验的记录数减去MASTER中的记录数。

CRC_DIFF:1 if the CRC of the chunk on the replica is different than the CRC of the chunk on the master, else 0.如果校验值相同,则CRC_DIFF为0,否则为1。

上述test.test中CRC_DIFF中crc为0的原因是SLAVE比MASTER多1000000条记录,且这1000000条记录正好又是在最后一个chunk中(如下所示)。注:master中id最大值为1000000。

 Query     REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'test', 'test', '', 'PRIMARY', '', NULL, COUNT(*), '' FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`id` > '')) ORDER BY `id` /*past upper chunk*/

CHUNK_INDEX:用于将table切割成chunk的索引。

LOWER_BOUNDARY:The index values that define the lower boundary of the chunk.

UPPER_BOUNDARY:The index values that define the upper boundary of the chunk.

上述两个参数可用来定位具有不同checksum值的chunk对应的索引的上限和下限。

总结

1. pt-table-checksum对表进行校验时,并不需要表上面有任何索引。这时候,整张表即是一个chunk。

REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'test2', 't1', '', NULL, NULL, NULL, 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 `test2`.`t1` /*checksum table*/ 

如果表的行过多的话,它会报如下错误:

# pt-table-checksum -h192.168.244.10 -umonitor -pmonitor123 --chunk-size-limit=100 --no-check-binlog-format --tables=test2.t1

11-17T11:41:53 Cannot checksum table test2.t1: There is no good index and the table is oversized. at /usr/local/bin/pt-table-checksum
line 6528.

解决方法:

调整chunk-size-limit的值

# pt-table-checksum -h192.168.244.10 -umonitor -pmonitor123 --chunk-size-limit=1000 --no-check-binlog-format --tables=test2.t1

            TS ERRORS  DIFFS     ROWS  CHUNKS SKIPPED    TIME TABLE
11-17T11:41:59 0 0 100004 1 0 0.600 test2.t1

pt-table-checksum的更多相关文章

  1. NXP ARM Vector Table CheckSum

    Signature Creator for NXP Cortex-M Devices Algorithm for creating the checksum The reserved Cortex-M ...

  2. DROP TABLE 恢复【一】

    当DROP TABLE指令敲下的时候,你很爽,你有考虑过后果么?如果该表真的没用,你DROP到无所谓,如果还有用的,这时你肯定吓惨了吧,如果你有备份,那么恭喜你,逃过一劫,如果没有备份呢?这时就该绝望 ...

  3. IAR EWARM Checksum Technical Note

    IELFTOOL Checksum - Basic actions EW targets: ARM, RH850, RX, SH, STM8 EW component: General issues ...

  4. 如何将MySQL help contents的内容有层次的输出

    经常会遇到这种情况,在一个不能上网的环境通过MySQL客户端登录数据库,想执行一个操作,却忘了操作的具体语法,各种不方便. 其实,MySQL数据库内置了帮助文档,通过help contents即可查看 ...

  5. Django添加Last-Modified和ETag

    用Django REST Framework做的REST API,其中有个API有这样的需求: APP端请求这个API,服务器端从数据库读数据,返回json.返回的数据量稍微有些大,但是可能一年才修改 ...

  6. MySQL没有备份怎么恢复被drop的表(利用undrop-for-innodb)

    介绍:     也许大家都难以理解,这么重要的数据为啥不备份(或者备份不可用)?而且还任性的drop table了.显然有备份是最好的,但是它们并不总是可用的.这种情况令人恐惧,但并非毫无希望.在许多 ...

  7. 无备份mysql删除表后恢复

    mysql从5.6.17开始自动设置innodb_file_per_table为on,每个表设置单独表空间,数据不是集中存放在ibdata1里.下面测试下无备份后drop表后的恢复. 前奏生成数据字典 ...

  8. 没有备份怎么恢复被drop的表(利用undrop-for-innodb)

    介绍:     也许大家都难以理解,这么重要的数据为啥不备份(或者备份不可用)?而且还任性的drop table了.显然有备份是最好的,但是它们并不总是可用的.这种情况令人恐惧,但并非毫无希望.在许多 ...

  9. swddude -- A SWD programmer for ARM Cortex microcontrollers.

    Introducing swddude I love the ARM Cortex-M series of microcontrollers.   The sheer computational po ...

  10. 用Qemu搭建x86_64学习环境

    作者信息 作者:彭东林 邮箱:pengdonglin137@163.com QQ:405728433 软件平台 主机: Ubuntu14.04 64位版本 模拟器:Qemu-2.8.0 Linux内核 ...

随机推荐

  1. 【探索】利用 canvas 实现数据压缩

    前言 HTTP 支持 GZip 压缩,可节省不少传输资源.但遗憾的是,只有下载才有,上传并不支持.如果上传也能压缩,那就完美了.特别适合大量文本提交的场合,比如博客园,就是很好的例子. 虽然标准不支持 ...

  2. 微软发布正式版SQL Server 2016

    微软于今天在SQL 官方博客上宣布 SQL Server 数据库软件的正式发布版本(GA),历时一年多,微软为该软件发布了多个公共预览版和候选版本,而今天最终版本终于上线了.在博客中,微软数据集团的企 ...

  3. ASP.NET MVC5+EF6+EasyUI 后台管理系统(74)-微信公众平台开发-自定义菜单

    系列目录 引言 1.如果不借用Senparc.Weixin SDK自定义菜单,编码起来,工作量是非常之大 2.但是借助SDK似乎一切都是简单得不要不要的 3.自定义菜单无需要建立数据库表 4.自定义菜 ...

  4. javascript之活灵活现的Array

    前言 就如同标题一样,这篇文章将会灵活的运行Array对象的一些方法来实现看上去较复杂的应用. 大家都知道Array实例有这四个方法:push.pop.shift.unshift.大家也都知道 pus ...

  5. MySQL 系列(四)主从复制、备份恢复方案生产环境实战

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...

  6. PHP类和对象之重载

    PHP中的重载指的是动态的创建属性与方法,是通过魔术方法来实现的.属性的重载通过__set,__get,__isset,__unset来分别实现对不存在属性的赋值.读取.判断属性是否设置.销毁属性. ...

  7. 计算Div标签内Checkbox个数或已被disabled的个数

    先看下面的html: 计算div内的checkbox个数:$('#divmod input[type="checkbox"]').length 计算div内checkbox被dis ...

  8. 说说BPM数据表和日志表中几个状态字段的详细解释

    有个客户说需要根据这些字段的值作为判断条件做一些定制化需求,所以需要知道这些字段的名词解释,以及里面存储的值具体代表什么意思 我只好为你们整理奉上这些了! Open Work Sheet  0 Sav ...

  9. AFN解析器里的坑

    AFN框架是用来用来发送网络请求的,它的好处是可以自动给你解析JSON数据,还可以发送带参数的请求AFN框架还可以监测当前的网络状态,还支持HTTPS请求,分别对用的类为AFNetworkReacha ...

  10. Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager

    Redis缓存服务器是一款key/value数据库,读110000次/s,写81000次/s,因为是内存操作所以速度飞快,常见用法是存用户token.短信验证码等 官网显示Redis本身并没有Wind ...