公司數據中心從託管機房遷移到阿里雲,須要對mysql遷移(Replication)後的數據一致性進行校驗,但又不能對生產環境使用形成影響,pt-table-checksum 成爲了絕佳也是惟一的檢查工具。html
pt-table-checksum
是 Percona-Toolkit 的組件之一,用於檢測MySQL主、從庫的數據是否一致。其原理是在主庫執行基於statement的sql語句來生成主庫數據塊的checksum,把相同的sql語句傳遞到從庫執行,並在從庫上計算相同數據塊的checksum,最後,比較主從庫上相同數據塊的checksum值,由此判斷主從數據是否一致。檢測過程根據惟一索引將表按row切分爲塊(chunk),覺得單位計算,能夠避免鎖表。檢測時會自動判斷複製延遲、 master的負載, 超過閥值後會自動將檢測暫停,減少對線上服務的影響。mysql
pt-table-checksum
默認狀況下能夠應對絕大部分場景,官方說,即便上千個庫、上萬億的行,它依然能夠很好的工做,這源自於設計很簡單,一次檢查一個表,不須要太多的內存和多餘的操做;必要時,pt-table-checksum
會根據服務器負載動態改變 chunk 大小,減小從庫的延遲。sql
爲了減小對數據庫的干預,pt-table-checksum
還會自動偵測並鏈接到從庫,固然若是失敗,能夠指定--recursion-method
選項來告訴從庫在哪裏。它的易用性還體如今,複製如有延遲,在從庫 checksum 會暫停直到遇上主庫的計算時間點(也經過選項--
設定一個可容忍的延遲最大值,超過這個值也認爲不一致)。 數據庫
爲了保證主數據庫服務的安全,該工具實現了許多保護措施:安全
自動設置 innodb_lock_wait_timeout
爲1s,避免引發服務器
默認當數據庫有25個以上的併發查詢時,pt-table-checksum
會暫停。能夠設置 --max-load
選項來設置這個閥值併發
當用 Ctrl+C 中止任務後,工具會正常的完成當前 chunk 檢測,下次使用 --resume
選項啓動能夠恢復繼續下一個 chunk工具
直接看 nettedfish 的說明:性能
1\. 鏈接到主庫:pt工具鏈接到主庫,而後自動發現主庫的全部從庫。默認採用show full processlist來查找從庫,可是這隻有在主從實例端口相同的狀況下才有效。
3\. 查找主庫或者從庫是否有複製過濾規則:這是爲了安全而默認檢查的選項。你能夠關閉這個檢查,可是這可能致使checksum的sql語句要麼不會同步到從庫,要麼到了從庫發現從庫沒有要被checksum的表,這都會致使從庫同步卡庫。
5\. 開始獲取表,一個個的計算。
6\. 若是是表的第一個chunk,那麼chunk-size通常爲1000;若是不是表的第一個chunk,那麼採用19步中分析出的結果。
7\. 檢查表結構,進行數據類型轉換等,生成checksum的sql語句。
8\. 根據表上的索引和數據的分佈,選擇最合適的split表的方法。
9\. 開始checksum表。
10\. 默認在chunk一個表以前,先刪除上次這個表相關的計算結果。除非–resume。
14\. 根據explain的結果,判斷chunk的size是否超過了你定義的chunk-size的上限。若是超過了,爲了避免影響線上性能,這個chunk將被忽略。
15\. 把要checksum的行加上for update鎖,並計算。
17-18\. 把計算結果存儲到master_crc master_count列中。
19\. 調整下一個chunk的大小。
20\. 等待從庫追上主庫。若是沒有延遲備份的從庫在運行,最好檢查全部的從庫,若是發現延遲最大的從庫延遲超過max-lag秒,pt工具在這裏將暫停。
21\. 若是發現主庫的max-load超過某個閾值,pt工具在這裏將暫停。
22\. 繼續下一個chunk,直到這個table被chunk完畢。
23-24\. 等待從庫執行完checksum,便於生成彙總的統計結果。每一個表彙總並統計一次。
25-26\. 循環每一個表,直到結束。this
校驗結束後,在每一個從庫上,執行以下的sql語句便可看到是否有主從不一致發生:
select * from percona.checksums where master_cnt <> this_cnt OR master_crc <> this_crc OR ISNULL(master_crc) <> ISNULL(this_crc) \G
--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 。
可是咱們檢查使用的mysql用戶通常是沒有 create table 權限的,因此你可能須要先手動建立:
CREATE DATABASE IF NOT EXISTS percona; 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;
生產環境中數據庫用戶權限通常都是有嚴格管理的,假如鏈接用戶是repl_user
(即直接用複製用戶來檢查),它應該額外賦予對其它庫的 SELECT ,LOCK TABLES 權限,若是後續要用 pt-table-sync 就就須要寫權限了。對percona庫有寫權限:
GRANT ALL PRIVILEGEES on percona.* to repl_user@'%' IDENTIFIED BY 'repl_pass'; GRANT SELECT,LOCK TABLES,PROCESS,SUPER on *.* to repl_user@'%';
注:
爲了減小沒必要要的麻煩,確保你的 repl_user@'xxx' 用戶能同時登錄主庫和從庫
--create-replicate-table
選項會自動建立 percona.checksums 表,但也意味着賦予額外的 CREATE TABLE
權限給 percona_tk@'xxx' 用戶。默認yes
PROCESS用於自動發現從庫信息,SUPER權限用於set binlog_format。
--no-check-replication-filters
表示不須要檢查 Master 配置裏是否指定了 Filter。 默認會檢查,若是配置了 Filter,如 replicate_do_db,replicate-wild-ignore-table,binlog_ignore_db 等,在從庫checksum就與遇到表不存在而報錯退出,因此官方默認是yes(--check-replication-filters
)但咱們實際在檢測中時指定--databases=
,因此就不存在這個問題,乾脆不檢測
--empty-replicate-table
:每一個表checksum開始前,清空它以前的檢測數據(不影響其它表的checksum數據),默認yes。固然若是使用--resume
啓動檢測數據不會清空。
當啓用--noempty-replicate-table
即不清空時,不計算計算chunk,只計算。
--databases=
,-d
:要檢查的數據庫,逗號分隔。用腳趾頭想也知道 --databases-regex
正則匹配要檢測的數據庫,--ignore-databases[-regex]
忽略檢查的庫。Filter選項。
--tables=
,-t
:要檢查的表,逗號分隔。若是要檢查的表分佈在不一樣的db中,能夠用--tables=dbname1.table1,dbnamd2.table2
的形式。同理有--tables-regex
,--ignore-tables
,--ignore-tables-regex
。--replicate
指定的checksum表始終會被過濾。
--recursion-method
:發現從庫的方式。pt-table-checksum 默承認以在主庫的 processlist
中找到從庫複製進程,從而識別出有哪些從庫,但若是使用是非標準3306端口,會致使找不到從庫信息。此時就會自動採用host
方式,但須要提早在從庫 my.cnf 裏面配置report_host
、report_port
信息,如:
report_host = MASTER_HOST report_port = 13306
最終極的辦法是dsn
,dsn指定的是某個表(如 percona.dsns ),錶行記錄是改主庫的(多個)從庫的鏈接信息。適用如下任一情形:
主庫不能自動發現從庫
不想在從庫添加額外配置(由於要重啓)
主從檢測鏈接用戶信息不同
多個從庫時只想驗證指定從庫的一致
我比較傾向使用DSN的方式。這個dsns表只須要在執行 pt-table-checksum
命令的服務器上可以訪問到就行。這裏糾正一個認識,網上不少人說 pt-table-checksum 要在主庫上執行,其實不是的,個人mysql實例比較多,只需在某一臺服務器上安裝percona-toolkit,這臺服務可以同時訪問主庫和從庫就好了。具體用法見後面實例。
場景:
標準端口3306,只檢查某一個庫的關鍵表
一主一從,binlog不是ROW模式
同網段複製,percona_tk@'192.168.5.%' 具有該有的權限:
GRANT ALL PRIVILEGEES on repl_user.* to repl_user@'192.168.5.%' IDENTIFIED BY 'repl_pass'; GRANT SELECT,LOCK TABLES,PROCESS,SUPER on *.* to repl_user@'192.168.5.%';
這是最簡單的方式,把要鏈接和檢查的信息交代就好了:
# pt-table-checksum h=MASTER_HOST,u=repl_user,p='repl_pass',P=3306 \ --databases=d_ts_profile --tables=t_user,t_user_detail,t_user_group --nocheck-replication-filters
若是是首次運行,會在主庫自動建立 percona.checksums 表。
輸出結果:
Replica lag is 2307 seconds on mysql-5. Waiting. Checksumming d_ts_profile.t_user_account: 3% 54:48 remain TS ERRORS DIFFS ROWS CHUNKS SKIPPED TIME TABLE 12-18T16:07:48 0 0 313641 9 0 146.417 d_ts_profile.t_user 12-18T16:08:00 0 0 397734 12 0 11.747 d_ts_profile.t_user_detail 12-18T16:08:24 0 0 1668327 20 0 23.941 d_ts_profile.t_user_group
TS :完成檢查的時間戳。
ERRORS :檢查時候發生錯誤和警告的數量。
DIFFS :不一致的chunk數量。當指定 --no-replicate-check
即檢查完但不當即輸出結果時,會一直爲0;當指定 --replicate-check-only
即不檢查只從checksums表中計算crc32,且只顯示不一致的信息(畢竟輸出的大部分應該是一致的,容易形成干擾)。
ROWS :比對的錶行數。
CHUNKS :被劃分到表中的塊的數目。
SKIPPED :因爲錯誤或警告或過大,則跳過塊的數目。
TIME :執行的時間。
TABLE :被檢查的表名
場景:
非標準端口13306,只檢查以 d_ts 開頭的全部庫
一主二從,binlog是ROW模式,其中一從在阿里雲ECS上,主庫是沒法直接訪問該從庫的
檢測用的帳號由於不是%,因此不同
如下是我環境的狀況
MASTER_HOST:13306 主庫
REPLICA_HOST:3306 從庫
PTCHECK_HOST pt-table-checksum所在服務器
DSN_DBHOST,記錄從庫(鏈接)dsns的數據庫
最優的方式就是dsn指定從庫了。在從庫或從庫同網段主機裏裝上 percona-toolkit。
在DSN_DBHOST 數據庫實例上建立DSNs表:
create database percona; 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`) ); GRANT ALL PRIVILEGEES on percona.* to percona_tk@'PTCHECK_HOST' IDENTIFIED BY 'percona_pass';
若是有多個實例要檢查,能夠建立多個相似的dsns表。上面的percona_tk用戶只是用來訪問dsn庫。插入從庫信息:
use percona; insert into dsns(dsn) values('h=REPLICA_HOST,P=3306,u=repl_user,p=repl_pass');
DSNs記錄 dsn 列格式如 h=REPLICA_HOST,u=repl_user,p=repl_pass
在 PTCHECK_HOST 上執行檢查命令:
# pt-table-checksum --replicate=percona.checksums --nocheck-replication-filters --no-check-binlog-format \ h=MASTER_HOST,u=repl_user,p='repl_pass',P=13306 --databases-regex=d_ts.* \ --recursion-method dsn=h=DSN_DBHOST,u=percona_tk,p='percona_pass',P=3306,D=percona,t=dsn
選項的意思就很少說了。
檢測完若是一致,實際上是求個心安,特別是在作數據遷移的時候。若是不一致,那就須要藉助 pt-table-sync
工具了,不做介紹。
Diffs cannot be detected because no slaves were found
不能自動找到從庫,確認processlist或host或dsns方式用對了。
Cannot connect to h=slave1.*.com,p=...,u=percona_user
能夠在pt-table-checksum
命令前加PTDEBUG=1
來看詳細的執行過程,如端口、用戶名、權限錯誤。
Waiting for the --replicate table to replicate to XXX
問題出在 percona.checksums 表在從庫不存在,根本緣由是沒有從主庫同步過來,因此看一下從庫是否延遲嚴重。
Pausing because Threads_running=25
反覆打印出相似上面中止檢查的信息。這是由於當前數據庫正在運行的線程數大於默認25,pt-table-checksum 爲了減小對庫的壓力暫停檢查了。等數據庫壓力過了就行了,或者也能夠直接 Ctrl+C 終端,下一次加上--resume
繼續執行,或者加大--max-load=
值。
字符集問題
Error checksumming table Error executing checksum query: DBD::mysql::st execute failed: Illegal mix of collations 12-17T14:48:04 Error checksumming table d_ec_cs.t_online_cs: Error executing checksum query: DBD::mysql::st execute failed: Illegal mix of collations for operation 'concat_ws' [for Statement "REPLACE INTO `percona`.`ali_checksum` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `f_cs_id`, `f_corp_id`, `f_valid`, `f_show_name`, `f_online_msg`, `f_offline_msg`, `f_show_mobile`, `f_group_id`, `f_qq`, `f_show_qq`, `f_msn`, `f_show_msn`, `f_sms_online`, `f_scheme`, `f_tel`, `f_telno`, `f_show_tel`, `f_contact`, `f_mobile`, `f_position`, `f_other1`, `f_other2`, `f_other_text1`, `f_other_text2`, `f_email`, `f_qq_first`, `f_qq_first_type`, `f_aids_open`, `f_aids_qq`, `f_aids_crmqq`, `f_aids_yahoo`, `f_aids_skype`, `f_aids_aliww`, `f_aids_msn`, `f_aids_alibaba`, `f_aids_alitrade`, CONCAT(ISNULL(`f_show_name`), ISNULL(`f_group_id`), ISNULL(`f_qq`), ISNULL(`f_show_qq`), ISNULL(`f_sms_online`), ISNULL(`f_other_text1`), ISNULL(`f_other_text2`), ISNULL(`f_email`)) )) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `d_ec_cs`.`t_online_cs` /*checksum table*/" with ParamValues: 0='d_ts_profile', 1='t_user_account', 2=1, 3=undef, 4=undef, 5=undef] at /usr/bin/pt-table-checksum line 10520.
是個bug,暫時沒法解決,Illegal mix of collations for operation 'concat_ws'。