在基於MySQL邏輯複製原理的下的主從架構,常常會因爲某些緣故產生主從數據不一致,從而致使主從複製進程報錯中斷。而基於按期去檢查從庫的show slave status\G的IO線程和SQL線程的狀態,只能確認當前replication是正常的,卻沒法確認當前主從數據是否一致。幸虧percona公司提供pt工具包,其中的pt-table-checksum和pt-table-sync相互配合,在基於必定的前提條件下,能夠較好的完成主從數據一致性校驗和修復,而不會較大程度上影響線上數據庫的性能。mysql
pt-table-checksum的官方文檔介紹以下: sql
pt-table-checksum performs an online replication consistency check by executing checksum queries on the master, which produces different results on replicas that are inconsistent with the master. The optional DSN specifies the master host. The tool’s 「EXIT STATUS」 is non-zero if any differences are found, or if any warnings or errors occur. The following command will connect to the replication master on localhost, checksum every table, and report the results on every detected replica: pt-table-checksum This tool is focused on finding data differences efficiently. If any data is different, you can resolve the problem with pt-table-sync.
pt-table-checksum其實做爲校驗工具,只負責檢測數據的不一致。至於差別數據的修復,而交由pt-table-sync去處理。數據庫
使用pt-table-checksum和pt-table-sync工具的前提條件:架構
一、表必須有主鍵or惟一索引
ide
二、要求binlog格式爲statement。若是線上數據庫採用的是binlog日誌格式是row的話,能夠加 --no-check-binlog-format來規避。工具
三、不能有存儲過程、觸發器、event
性能
四、不建議修復有外鍵約束的表測試
pt-table-checksum原理能夠查閱官方文檔或者在測試環境下開啓general_log,執行一次pt-table-checksum後翻查其生成的日誌便可。基本原理就是在主庫建立一個checksums表,存放每一個chunk的校驗值。經過將表按照主鍵or惟一索引進行排序,按自適應的行記錄數生成若干個chunk,將每一個行記錄串起來轉成字符串,計算CRC32值,而後將該chunk的校驗值記錄到checksums表中。而這些SQL操做都會以statement的方式傳送到從庫從而執行相同的操做,若是表的數據有不一致的狀況,相應的chunk的校驗值也會不一致。this
校驗&修復的腳本以下:線程
#!/bin/sh ##單向主從架構的話,master_ip是主庫的ip地址,slave_ip是從庫的ip地址;雙向主從架構的話,master_ip是以本庫數據爲準的主庫ip地址,slave_ip是數據被修正的備選主庫ip地址。 master_ip="192.168.124.131" slave_ip="192.168.124.132" port="3306" user="checksums" password="checksums" pt_sync="/usr/bin/pt-table-sync" pt_check="/usr/bin/pt-table-checksum" mysql="/usr/local/mysql/bin/mysql" mysql_master="$mysql -u$user -p$password -h$master_ip -P$port" mysql_slave="$mysql -u$user -p$password -h$slave_ip -P$port -N " table_file="/tmp/table.txt" diff_table="/tmp/diff.txt" sync_sql="/tmp/sync.sql" ### 清理環境 ### if [ -e $table_file ] then rm -fr $table_file fi if [ -e $diff_table ] then rm -fr $diff_table fi if [ -e $sync_sql ] then rm -fr $sync_sql fi ### 初始化checksums表 ### $mysql_master << EOF >/dev/null 2>&1 CREATE DATABASE IF NOT EXISTS PERCONA; USE PERCONA; CREATE TABLE IF NOT EXISTS 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; EOF ### 過濾出不包含外鍵約束、擁有主鍵or惟一索引的Innodb表。而觸發器、存儲過程和event須要人工自行過濾掉所涉及的表 ### $mysql_master << EOF >/dev/null 2>&1 select t.TABLE_SCHEMA,t.TABLE_NAME from information_schema.tables t inner join information_schema.statistics s on t.TABLE_SCHEMA=s.TABLE_SCHEMA and t.TABLE_NAME=s.TABLE_NAME inner join information_schema.key_column_usage k on t.TABLE_SCHEMA=k.TABLE_SCHEMA and t.TABLE_NAME=k.TABLE_NAME where t.TABLE_TYPE='BASE TABLE' and t.ENGINE='InnoDB' and s.NON_UNIQUE=0 and k.POSITION_IN_UNIQUE_CONSTRAINT is null and concat(k.TABLE_SCHEMA,'.',k.TABLE_NAME) not in (select concat(k.TABLE_SCHEMA,'.',k.TABLE_NAME) from information_schema.key_column_usage k where k.POSITION_IN_UNIQUE_CONSTRAINT is not null) and t.TABLE_SCHEMA not in ('mysql','percona','sys','information_schema','performance_schema') group by t.TABLE_SCHEMA,t.TABLE_NAME into outfile "$table_file" FIELDS TERMINATED BY '|' LINES TERMINATED BY '\n'; EOF ### 調用pt-table-checksum,作數據差別比對,將結果寫入percona.checksums表 ### for i in $(cat $table_file) do db=$(echo $i|awk -F\| '{print $1}') tb=$(echo $i|awk -F\| '{print $2}') $pt_check --set-vars innodb_lock_wait_timeout=120,binlog_format='statement' -u$user -p$password -h$master_ip -P$port --databases=$db --tables=$tb >/dev/null 2>&1 done ### 在slave端拼接生成修復的命令集,而後執行生成相應的SQL語句 $mysql_slave << EOF 1>$diff_table 2>/dev/null SELECT concat(db,'|',tbl) FROM percona.checksums where ( master_cnt <> this_cnt or master_crc <> this_crc or ISNULL(master_crc)<>ISNULL(this_crc)) GROUP BY db, tbl ; EOF for i in $(cat $diff_table) do db=$(echo $i|awk -F\| '{print $1}') tb=$(echo $i|awk -F\| '{print $2}') $pt_sync --print --sync-to-master h=$slave_ip,P=$port,u=$user,p="$password" --databases="$db" --tables="$tb" >> $sync_sql done ### 在master側執行差別SQL,經過複製修復slave側的數據差別 ### $mysql_master << EOF >/dev/null 2>&1 set tx_isolation="REPEATABLE-READ"; set binlog_format=statement; source $sync_sql; EOF ## 清理臨時文件 ### rm -fr $sync_sql $table_file $diff_table
執行該腳本以前,須要知足幾個前提:
一、建立專用的賬號用於校驗和修復。
賬號建立語句:GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, PROCESS, FILE, SUPER, REPLICATION SLAVE ON *.* TO 'checksums'@'%'
PS:若是checksums用戶的登陸IP有限制的話,能夠只配置主庫和從庫的IP便可。
二、目前腳本只能自動過濾出擁有惟一索引or主鍵、不帶外鍵約束的innodb表,有觸發器、存儲過程和event所涉及的表,須要人工剔除。
三、該腳本只需部署在主庫側便可。不須要部署在從庫側。