項目地址:https://github.com/seanlook/p...python
主從環境下數據一致性校驗常常會用 pt-table-checksum 工具,它的原理及實施過程以前寫過一篇文章:生產環境使用 pt-table-checksum 檢查MySQL數據一致性。可是DBA工做中還會有些針對兩個表檢查是否一致,而這兩個表之間並無主從關係,pt工具是基於binlog把在主庫進行的檢查動做,在從庫重放一遍,此時就不適用了。mysql
總會有這樣特殊的需求,好比從阿里雲RDS實例遷移到自建mysql實例,它的數據傳輸服務實現方式是基於表的批量數據提取,加上binlog訂閱,但強制row模式會致使pt-table-checksum沒有權限把會話臨時改爲statement。另外一種需求是,整庫進行字符集轉換:庫表定義都是utf8,但應用鏈接使用了默認的 latin1,要將鏈接字符集和表字符集統一塊兒來,只能以latin1導出數據,再以utf8導入,這種狀況數據一致性校驗,且不說binlog解析程序不支持statement(如canal),新舊庫自己內容不一樣,pt-table-checksum 算出的校驗值也會不同,失效。git
因此才萌生了參考 pt-table-checksum 本身寫了一個:px-table-checksum 。github
總體思路是借鑑pt-table-checksum,從源庫批量(即chunk)取出一塊數據如1000行,計算CRC32值,一樣的語句在目標庫運行一遍,結果都存入另外一個庫,最後檢查對應編號的chunk crc值是否一致。知道不一致還不行,得可否快速方便的修復差別,因此繼續根據那些不一致的chunk,去目標庫和源庫找到不一致的行,是缺失,仍是多餘,仍是被修改了,而後生成修復sql,根據指示是否自動修復。redis
那麼問題就在於:sql
如何肯定批次,也就是下一個chunk該怎麼取?
我還沒想作到pt-table-checksum那樣,能夠根據負載動態調整chunk大小,甚至活躍線程數超過閥值就暫停檢查,上來工做量就太大了。目前每次計算的chunk的行數是固定的,能夠配置1000或2000等。數據庫
因此就要用到分頁查詢,根據(自增或聯合)主鍵、惟一索引,每次limit 1000後升序取最後一條,做爲下一批的起始。因此要分析表上的鍵狀況,組合查詢條件。目前僅能檢查有主鍵或惟一因此的表。多線程
如何保證源庫和目標庫,運行的sql同樣?
以前一版是目標庫和源庫,以多線程各自計算chunk,入庫,後來才意識到嚴重的bug:好比一樣是取1000行,若是目標庫少數據,那麼下一個chunk起始就不同,比較的結果簡直一塌糊塗。函數
因此必須保證相同編號的chunk,起點必須相同,因此想到用隊列,存放在源庫跑過的全部校驗sql,模擬pt工具在目標庫重放。考慮到要多線程同時比較多個表,隊列可能吃內存過大,因而使用了redis隊列。工具
直接在數據庫中計算crc32,仍是取出數據在內存裏計算?
翻了pt-table-checksum的源碼,它是在數據庫裏計算的。可是第一節裏說過,若是目標庫和源庫要使用不一樣的字符集才能讀出正確的數據,只能查詢出來以後再比較。因此 px-table-checksum 兩種都支持,只需指定一個配置項。
同時檢查多個表,源庫sql擠在隊列,目標庫拿出來執行時過了1s,此時源庫那條數據又被修改了一次同步到了目標庫,會致使計算結果不一致,實則一致,怎麼處理
沒法處理,是px-table-checksum相比pt-table-checksum最大的缺陷。
但爲了儘量減小此類問題(好比主從延遲也可能會),特地設計了多個redis隊列,目標庫多個檢查線程,即好比同時指定檢查8個表,源庫檢查會有8個線程對應,但能夠根據表的寫入狀況,配置4個redis隊列(目前是隨機入列),10個目標庫檢查線程,來減小不許確因素。
但站在個人角度每每來講,不一致的數據會被記錄下來,若是很少,人工覈對一下;若是較多,就再跑一遍檢查,若是兩次都有同一條數據不一致,那就有狀況了。
若是檢查期間源表數據,變化頻繁,有可能檢查的結果不許確
也就是上面第4點的問題。很明顯,這個程序每一個檢查的事務是分開的,不像pt工具能嚴格保證每條檢查sql的事務順序。但有不一致的數據再排查一下就ok了。實際在我線上使用過程當中,99.9%是準確的。
表上必須有主鍵或惟一索引
程序會檢查,若是沒有會退出。
varbinay,blob等二進制字段不支持修復
其實也不是徹底不支持,要看怎麼用的。開發若是有把字符先轉成字節,再存入mysql,這種就不支持修復。是有辦法能夠處理,那就是從源庫查時用 hex()
函數,修復sql裏面unhex()
寫回去。
該python程序基於2.7開發,2.六、3.x上沒有測試。使用前須要安裝 MySQLdb
和hotqueue
:
$ sudo pip install MySQL-python hotqueue
要比較的表和選項,使用全配置化,即不經過命令行的方式指定(原諒命令行參數使用方式會額外增長代碼量)。
px-table-checksum.py
主程序,運行python px-table-checksum.py
執行一致性檢查,但必定了解下面的配置文件選項。
settings_checksum.py
配置選項
CHUNK_SIZE
: 每次提取的chunk行數
REDIS_INFO
: 指定使用redis隊列地址
REDIS_QUEUE_CNT
: redis隊列數量,消費者(目標庫)有一一對應的線程守着隊列
REDIS_POOL_CNT
: 生產者(源庫)redis客戶端鏈接池。這個設計是爲了緩解GIL帶來的問題,把入列端與出列端分開,由於若是表多可能短期有大量sql入隊列,避免hotqueue爭用
CALC_CRC32_DB
: True 表示在db裏面計算checksum值,False表示取出chunk數據在python裏面計算。默認給的值是根據鏈接字符集定的。
DO_COMPARE
: 運行模式
0: 只提取數據計算,不比較是否一致。能夠在以後在模式2下只比較
1: 計算,並比較。經常使用,每次計算以前會刪除上一次這個待檢查表的結果,比較的結果只告訴哪些chunk號不一致。
2: 不計算,只從t_checkum結果比較。經常使用,計算是消耗數據庫資源的,能夠只對已有的checksum計算結果比較不一致的地方。相似pt工具的--replicate-check-only
選項。
GEN_DATAFIX
:
與DO_COMPARE
結合使用,爲 True 表示對不一致的chunk找到具體不一致行,並生成修復sql;爲 False 則什麼都不作。
RUN_DATAFIX
:
與GEN_DATAFIX
結合使用,爲 True 表示對生成的修復sql,在目標庫執行。須要謹慎,若是哪一次設置了修復,記得完成後改回False,否則下次檢查另外一個表就出意外了,因此特地對這個選項再加了一個確認提示。
DB_CHECKSUM
: 一個字典,指定checksum的結果存到哪裏
配置文件有示例,必須指定 db_name
,表會自動建立。
settings_cs_tables.py
上面的配置文件能夠認爲是用於控制程序的,這個配置文件是指定要校驗的源庫和目標庫信息,以及要檢驗哪些表。
TABLES_CHECK
: 字典,指定要檢查哪些表的一致性,db名爲key,多個table名組成列表爲value。暫不支持對整個db作檢查,同時比較的表數量不建議超過8個
DB_SOURCE
: 字典,指定源庫的鏈接信息
DB_SOURCE
: 字典,指定目標庫的鏈接信息