1. 程式人生 > 其它 >技術分享 | 資料校驗工具 pt-table-checksum

技術分享 | 資料校驗工具 pt-table-checksum

技術標籤:技術分享校驗資料一致性

作者:耿進
愛可生 DBA 團隊成員,負責公司 DMP 產品的運維和客戶 MySQL 問題的處理。對資料庫技術有著濃厚的興趣。你見過凌晨四點 MySQL 的 error 嗎?
本文來源:原創投稿
*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。


參考文件:https://www.percona.com/doc/percona-toolkit/LATEST/pt-table-checksum.html

1. 概述

pt-table-checksum 是 Percona-Toolkit 的元件之一,用於檢測 MySQL 主、從庫的資料是否一致。其原理是在主庫執行基於 statement 的 SQL 語句來生成主庫資料塊的checksum,把相同的 SQL 語句傳遞到從庫執行,並在從庫上計算相同資料塊的 checksum,最後,比較主從庫上相同資料塊的 checksum 值,由此判斷主從資料是否一致。它能在非常大的表上工作的一個原因是,它把每個表分成行塊,並檢查每個塊與單個替換。選擇查詢。它改變塊的大小,使校驗和查詢在所需的時間內執行。分塊表的目的是確保校驗和不受干擾,並且不會在伺服器上造成太多複製延遲或負載,而不是使用單個大查詢處理每個表。這就是為什麼預設情況下每個塊的目標時間是 0.5 秒。

2. 場景

pt-table-checksum 預設情況下可以應對絕對部分場景,官方說,即使上千個庫、上萬億的行,它依然可以很好的工作,這源自於設計很簡單,一次檢查⼀個表,不需要太多的記憶體和多餘的操作;必要時,pt-table-checksum 會根據伺服器負載動態改變 chunk 大小,減少從庫的延遲。
為了減少對資料庫的干預,pt-table-checksum 還會⾃動偵測並連線到從庫,當然如果失敗,可以指定 --recursion-method 選項來告訴從庫在哪裡。它的易用性還體現在,複製若有延遲,在從庫 checksum 會暫停直到趕上主庫的計算時間點(也通過選項 – 設定一個可容忍的延遲最大值,超過這個值也認為不一致)。

3. 保障措施

pt-table-checksum 有許多其他的安全措施,以確保它不會⼲擾任何伺服器的操作,包括副本。為了做到這⼀點,pt-table-checksum 檢測副本並⾃動連線到它們。(如果失敗,可以使⽤遞迴⽅法選項給它⼀個提示。)
該工具持續監控副本。如果任何副本在複製過程中遠遠落後,pt 表校驗和會暫停以使其趕上來。如果任何副本有錯誤,或者複製停止,pt-table 校驗和將暫停並等待。此外,pt-table-checksum 查詢問題的常見原因,比如複製過濾器,並且拒絕操作,除⾮您強迫它這樣做。複製篩選器是危險的,因為 pt-table-checksum 執行的查詢可能與它們發生衝突,並導致複製失敗。

pt-table 校驗和驗證塊是否太大而不能安全校驗和。它對每個塊執行解釋查詢,並跳過可能大於所需行數的塊。您可以使用 --chunk-size-limit 選項配置此保護措施的敏感性。如果一個表因為行數少而要在單個塊中對其進行校驗,那麼 pt-table-checksum 將額外驗證該表在副本上是否過大。這避免了以下場景:表在主伺服器上是空的,但在副本上非常大,並且在一個大型查詢中進行檢查,這會導致複製過程中出現非常長的延遲。
還有⼀些其他的保障措施。例如,pt-table-checksum 將它的會話級 innodb_lock_wait_timeout 設定為 1 秒,這樣,如果存在鎖等待,它將成為受害者,而不是導致其他查詢超時。另一個安全措施檢查資料庫伺服器上的負載,如果負載過高則暫停。對於如何做到這一點,沒有一個正確的答案,但是預設情況下,如果有超過 25 個併發執行的查詢,pt-table-checksum 將暫停。您可能應該使用 --max-load 選項為伺服器設定一個合理的值。
校驗和通常是一個低優先順序的任務,應該讓位給伺服器上的其他⼯作。然而,一個必須經常重啟的共工具是很難使用的。因此,pt 表校驗和對錯誤具有很強的彈性。例如,如果資料庫管理員出於任何原因需要殺死 pt-table-checksum 的查詢,這就不是一個致命錯誤。⽤戶經常執行 pt-kill 來終止任何長時間執行的校驗和查詢。該工具將重試一次已殺死的查詢,如果再次失敗,它將移動到該表的下一個塊。如果存在鎖等待超時,則應用相同的行為。如果發生這樣的錯誤,工具將列印一個警告,但每個表只打印一次。如果到任何伺服器的連線失敗,pt-table-checksum 將嘗試重新連線並繼續⼯作。

4. 操作步驟

1. 建立主從架構

安裝 mysql(略)

# 建立複製⽤戶(⽅便切換,從庫也建立)
master82 >GRANT REPLICATION SLAVE ON *.* TO [email protected]'10.186.63.%' IDENTIFIED BY '123';
Query OK, 0 rows affected, 1 warning (0.01 sec)
# 建⽴複製
slave83 >change master to
master_host='10.186.63.82',master_port=4380,master_user='repl',master_password='123',MASTER_AUTO_POSITION=1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
slave83 >start slave;
Query OK, 0 rows affected (0.04 sec)
slave83 >show slave status\G
*************************** 1. row ***************************
 Slave_IO_State: Waiting for master to send event
 Master_Host: 10.186.63.82
 Master_User: repl
 Master_Port: 4380
 Connect_Retry: 60
 Master_Log_File: mysql-bin.000004
 Read_Master_Log_Pos: 220764474
 Relay_Log_File: mysql-relay.000002
 Relay_Log_Pos: 24944
 Relay_Master_Log_File: mysql-bin.000004
 Slave_IO_Running: Yes
 Slave_SQL_Running: Yes

2. 造資料

使用 sysbench 造表,並且會同步至從庫。

[[email protected] ~]# sysbench /usr/share/sysbench/oltp_read_write.lua --mysql-host=10.186.63.82 \
--mysql-port=4380 --mysql-user=gengjin --mysql-password=123 --mysql-db=test \
--table-size=1000000 --tables=10 --threads=50 --report-interval=3 --time=20 prepare
sysbench 1.0.17 (using system LuaJIT 2.0.4)
Initializing worker threads...
Creating table 'sbtest3'...
Creating table 'sbtest6'...
Creating table 'sbtest5'...
Creating table 'sbtest8'...
Creating table 'sbtest1'...
Creating table 'sbtest9'...
Creating table 'sbtest7'...
Creating table 'sbtest4'...
Creating table 'sbtest2'...
Creating table 'sbtest10'...
Inserting 1000000 records into 'sbtest1'
Inserting 1000000 records into 'sbtest8'
Inserting 1000000 records into 'sbtest9'
Inserting 1000000 records into 'sbtest6'
Inserting 1000000 records into 'sbtest7'
Inserting 1000000 records into 'sbtest5'
Inserting 1000000 records into 'sbtest4'
Inserting 1000000 records into 'sbtest3'
Inserting 1000000 records into 'sbtest2'
Inserting 1000000 records into 'sbtest10'
master82 >use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
master82 >show tables;
+----------------+
| Tables_in_test |
+----------------+
| sbtest1 |
| sbtest10 |
| sbtest2 |
| sbtest3 |
| sbtest4 |
| sbtest5 |
| sbtest6 |
| sbtest7 |
| sbtest8 |
| sbtest9 |
+----------------+
10 rows in set (0.00 sec)
master82 >
slave83 >use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
slave83 >show tables;
+----------------+
| Tables_in_test |
+----------------+
| sbtest1 |
| sbtest10 |
| sbtest2 |
| sbtest3 |
| sbtest4 |
| sbtest5 |
| sbtest6 |
| sbtest7 |
| sbtest8 |
| sbtest9 |
+----------------+
10 rows in set (0.00 sec)
slave83 >

3. 校驗

1)下載安裝 pt 工具

#下載
wget https://www.percona.com/downloads/percona-toolkit/3.1.0/binary/tarball/percona-toolkit-
3.1.0_x86_64.tar.gz
yum -y install perl-devel perl-Digest-MD5 perl-DBI perl-DBD-MySQL perl-IO-Socket-SSL.noarch perl-Time-HiRes
cd percona-toolkit-3.1.0/
perl Makefile.PL PREFIX=/usr/local/
make
make install

2)引數

–replicate-check:執行完 checksum 查詢在 percona.checksums 表中,不⼀定⻢上檢視結果呀 —— yes 則馬上比較 chunk 的 crc32 值並輸出 DIFFS 列,否則不輸出。預設 yes,如果指定為 --noreplicate-check,一般後續使用下面的 --replicate-check-only 去輸出 DIFF 結果。

–replicate-check-only:不在主從庫做 checksum 查詢,只在原有 percona.checksums 表中查詢結果,並輸出資料不⼀致的資訊。週期性的檢測⼀致性時可能⽤到。

–nocheck-binlog-format:不檢測日誌格式。這個選項對於 ROW 模式的複製很重要,因為 pt-table-checksum 會在 Master 和 Slave 上設定 binlog_format=STATEMENT(確保從庫也會執行 checksum SQL),MySQL 限制從庫是無法設定的,所以假如行復制從庫,再作為主庫複製出新從庫時(A->B->C),B 的 checksums 資料將無法傳輸。

–replicate= 指定 checksum 計算結果存到哪個庫表⾥,如果沒有指定,預設是 percona.checksums 。

3. 執行校驗

1)場景 1

  • 標準埠

  • 檢查某庫下某表

  • ⼀主⼀從

  • 從庫 binlog 不是 ROW 格式

[[email protected] percona-toolkit-3.1.0]# pt-table-checksum h=10.186.63.82,u=gengjin,p='123',P=3306 --
 databases=test --tables=sbtest1,sbtest2 --nocheck-replication-filters
Checking if all tables can be checksummed ...
Starting checksum ...
 TS ERRORS DIFFS ROWS DIFF_ROWS CHUNKS SKIPPED TIME TABLE
12-21T09:42:57 0 0 1000000 0 8 0 3.859 test.sbtest1
12-21T09:43:02 0 0 1000000 0 6 0 5.122 test.sbtest2
...
#會⾃動建立校驗庫表
master82 >show tables;
+-------------------+
| Tables_in_percona |
+-------------------+
| checksums |
+-------------------+
mysql> select * from checksums
 -> ;
+------+---------+-------+------------+-------------+----------------+----------------+----------+----------
+------------+------------+---------------------+
| db | tbl | chunk | chunk_time | chunk_index | lower_boundary | upper_boundary | this_crc | this_cnt
| master_crc | master_cnt | ts |
+------+---------+-------+------------+-------------+----------------+----------------+----------+----------
+------------+------------+---------------------+
| test | sbtest1 | 1 | 0.004363 | PRIMARY | 1 | 1000 | 949e9d20 | 1000
| 949e9d20 | 1000 | 2020-12-22 03:26:13 |
| test | sbtest1 | 2 | 0.282387 | PRIMARY | 1001 | 115598 | daeb5f19 | 114598
| daeb5f19 | 114598 | 2020-12-22 03:26:13 |
| test | sbtest1 | 3 | 0.382239 | PRIMARY | 115599 | 317495 | d748771b | 201897
| d748771b | 201897 | 2020-12-22 03:26:14 |
| test | sbtest1 | 4 | 0.462463 | PRIMARY | 317496 | 559251 | 2b9cc322 | 241756
| 2b9cc322 | 241756 | 2020-12-22 03:26:14 |
| test | sbtest1 | 5 | 0.43845 | PRIMARY | 559252 | 810981 | 1bef4fe1 | 251730
| 1bef4fe1 | 251730 | 2020-12-22 03:26:15 |
| test | sbtest1 | 6 | 0.337617 | PRIMARY | 810982 | 1000000 | 6daaef2b | 189019
| 6daaef2b | 189019 | 2020-12-22 03:26:15 |
| test | sbtest1 | 7 | 0.002212 | PRIMARY | NULL | 1 | 0 | 0
| 0 | 0 | 2020-12-22 03:26:15 |
| test | sbtest1 | 8 | 0.011642 | PRIMARY | 1000000 | NULL | 0 | 0
| 0 | 0 | 2020-12-22 03:26:15 |
| test | sbtest2 | 1 | 0.447947 | PRIMARY | 1 | 262120 | d454c57a | 262120
| d454c57a | 262120 | 2020-12-22 03:26:18 |
| test | sbtest2 | 2 | 0.507594 | PRIMARY | 262121 | 554699 | 221a4326 | 292579
| 221a4326 | 292579 | 2020-12-22 03:26:19 |
| test | sbtest2 | 3 | 0.497652 | PRIMARY | 554700 | 844644 | b47933a4 | 289945
| b47933a4 | 289945 | 2020-12-22 03:26:20 |
| test | sbtest2 | 4 | 0.286117 | PRIMARY | 844645 | 1000000 | 7246a964 | 155356
| 7246a964 | 155356 | 2020-12-22 03:26:20 |
| test | sbtest2 | 5 | 0.002235 | PRIMARY | NULL | 1 | 0 | 0
| 0 | 0 | 2020-12-22 03:26:20 |
| test | sbtest2 | 6 | 0.002173 | PRIMARY | 1000000 | NULL | 0 | 0
| 0 | 0 | 2020-12-22 03:26:20 |
+------+---------+-------+------------+-------------+----------------+----------------+----------+----------
+------------+------------+---------------------+
14 rows in set (0.01 sec)
mysql>

2)場景 2(dsn)

  • 非標準埠(主從埠不⼀致)

  • 一主多從

  • 全例項校驗

# 建立校驗庫表
master82 >CREATE DATABASE IF NOT EXISTS percona; 
Query OK, 1 row affected (0.00 sec)
master82 >CREATE TABLE IF NOT EXISTS percona.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; 
Query OK, 0 rows affected (0.06 sec)
# 建立dsn表
master82 >CREATE DATABASE IF NOT EXISTS percona; 
Query OK, 1 row affected, 1 warning (0.00 sec)
master82 >CREATE TABLE percona.dsns ( id int(11) NOT NULL AUTO_INCREMENT, parent_id int(11) DEFAULT NULL,
dsn varchar(255) NOT NULL, PRIMARY KEY (id) );
Query OK, 0 rows affected (0.01 sec)
master82 >show tables;
+-------------------+
| Tables_in_percona |
+-------------------+
| checksums |
| dsns |
+-------------------+
2 rows in set (0.00 sec)
# 新增從庫資訊
master82 >insert into percona.dsns(dsn) values('h=10.186.63.83,P=4380,u=gengjin,p=123');
Query OK, 1 row affected (0.00 sec)
master82 >
# 校驗
[email protected] percona-toolkit-3.1.0]# pt-table-checksum --replicate=percona.checksums --nocheck-replicationfilters --no-check-binlog-format --max-load Threads_connected=600 h=10.186.63.82,u=gengjin,p='123',P=4380 --
recursion-method dsn=h=10.186.63.83,u=gengjin,p='123',P=4380,D=percona,t=dsns --function MD5
Checking if all tables can be checksummed ...
Starting checksum ...
# A software update is available:
 TS ERRORS DIFFS ROWS DIFF_ROWS CHUNKS SKIPPED TIME TABLE
12-21T09:03:19 0 0 0 0 1 0 0.358 mysql.columns_priv
12-21T09:03:19 0 0 10 0 1 0 0.497 mysql.db
12-21T09:03:20 0 0 2 0 1 0 0.497 mysql.engine_cost
12-21T09:03:20 0 0 0 0 1 0 0.497 mysql.event
12-21T09:03:21 0 0 0 0 1 0 0.497 mysql.func
12-21T09:03:21 0 0 41 0 1 0 0.498 mysql.help_category
12-21T09:03:22 0 0 699 0 1 0 0.497 mysql.help_keyword
12-21T09:03:22 0 0 1413 0 1 0 0.497 mysql.help_relation
12-21T09:03:23 0 0 643 0 1 0 0.497 mysql.help_topic
12-21T09:03:23 0 0 0 0 1 0 0.498 mysql.ndb_binlog_index
12-21T09:03:24 0 0 0 0 1 0 0.498 mysql.plugin
12-21T09:03:24 0 0 48 0 1 0 0.494 mysql.proc
12-21T09:03:25 0 0 0 0 1 0 0.501 mysql.procs_priv
12-21T09:03:25 0 0 2 0 1 0 0.492 mysql.proxies_priv
12-21T09:03:26 0 0 6 0 1 0 0.498 mysql.server_cost
12-21T09:03:26 0 0 0 0 1 0 0.498 mysql.servers
12-21T09:03:27 0 0 2 0 1 0 0.497 mysql.tables_priv
12-21T09:03:27 0 0 0 0 1 0 0.498 mysql.time_zone
12-21T09:03:28 0 0 0 0 1 0 0.496 mysql.time_zone_leap_second
12-21T09:03:28 0 0 0 0 1 0 0.497 mysql.time_zone_name
12-21T09:03:29 0 0 0 0 1 0 0.499 mysql.time_zone_transition
12-21T09:03:30 0 0 0 0 1 0 0.510 mysql.time_zone_transition_type
12-21T09:03:30 0 1 8 0 1 0 0.481 mysql.user
12-21T09:03:30 0 0 1 0 1 0 0.485 percona.dsns
12-21T09:03:31 0 0 6 0 1 0 0.485 sys.sys_config
12-21T09:03:42 0 0 1000000 0 11 0 11.002 test.sbtest1
12-21T09:03:53 0 0 1000000 0 10 0 10.662 test.sbtest10
12-21T09:04:02 0 0 1000000 0 9 0 9.797 test.sbtest2
12-21T09:04:14 0 0 1000000 0 10 0 11.497 test.sbtest3
12-21T09:04:24 0 0 1000000 0 10 0 10.495 test.sbtest4
12-21T09:04:33 0 0 1000000 0 9 0 8.996 test.sbtest5
12-21T09:04:42 0 0 1000000 0 9 0 8.198 test.sbtest6
12-21T09:04:51 0 0 1000000 0 9 0 9.302 test.sbtest7
12-21T09:05:00 0 0 1000000 0 9 0 9.141 test.sbtest8
12-21T09:05:08 0 0 1000000 0 9 0 8.345 test.sbtest9
12-21T09:05:09 0 0 1 0 1 0 0.490 universe.u_delay
檢視checksums表資料(略)