1. 程式人生 > >資料庫 主從一致性檢查和修復

資料庫 主從一致性檢查和修復



http://blog.csdn.net/dba_waterbin/article/details/43608587

騰訊遊戲資料自愈服務方案簡介

1. 引言

在正式介紹專案背景之前,讓我們先看一組資料:

 

這是2個灰度的業務,都是Z3伺服器,我們先只從時間成本的收益角度來看:

⑴  左一業務資料量是330G,資料不一致時通過重做slave需要150分鐘左右,而藉助pt-table-sync只需要5分鐘,速度提升30 倍。

⑵  右一業務的量是93G,通過sync工具花費3分鐘,而如果重做slave35分鐘,速度提升12倍。

引入這組資料意在指明,整個過程不僅解放了DBA的雙手,也符合”零運維”的趨勢,資料自愈將是互娛DBA

團隊在未來提供的服務之一。

2. 背景

MySQL資料庫基於binlog的資料同步方案,絕大部分情況下能保證主備資料完全一致,但某些異常情況下,例如開發使用了unsafe statementSQL(如帶limit)及硬體故障等,都可能導致主備資料不一致。DBA通過checksum工具可以發現這些不一致的情況,但往往需要重新做一個熱備來恢復主備資料一致性,並且此過程可能需要10小時以上,而實際情況上主備資料通常僅有少量不一致,線上修復這些資料差異可以更高效地完成一個數據一致的熱備。

3. 收益

重做熱備是我們目前首選的修復方案,但有時候沒有備用的新機子卻讓修復步伐戛然而止,而如果沒有修復,倘若master

故障,由於資料不一致,切換到slave是存在資料丟失的風險,那麼又不得不執行修復,DBA就需要評估以前slave上的連線切換到master是否會影響master的效能......這樣DBA的工作量就無形中翻倍了。

我們在引言中也道出了sync工具相比傳統的熱備在時間上的收益,但除了這個,資料自愈服務的收益包括但不限於:

▼ 業務資料更安全,恢復熱備時間變短

▼ 減少伺服器資源,避免重做熱備的機器申請

▼ 提升DBA做熱備的處理效率

▼ 降低溝通成本,保證業務持續穩定執行

4. 資料自愈解決方案

我們從開源社群引入了Percona公司的pt-table-sync,該專案從2007

年啟動。

下面我們對該工具的實現細節以及互娛DBA團隊在此基礎上進行定製開發的部分內容進行討論。

⑴  流程圖

 

Pt-table-sync2種修復模式:replicate模式和非replicate模式,上圖是replicate的,這也是我們所推薦,原因會在下文說明。

這裡,我們先對上圖作下簡要介紹

① 對每一個chunk,再校驗時加上for update鎖,一旦獲得鎖,就記錄下當前主庫的show master status值。以我測試機的案例:

SELECT /*water2.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND ((1=1)) FOR UPDATE

SHOW MASTER STATUS

② 在從庫上執行select master_pos_wait()函式,等待從庫SQL執行緒執行到show master status得到的位置,以此保證,主從上關於這個chunk的內容均不再改變。

SELECT MASTER_POS_WAIT('binlog3306.000014', 139672350, 60)

③ 對這個chunk執行checksum,然後與主庫的checksum進行比較

DR

SELECT /*water2.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND ((1=1)) LOCK IN SHARE MODE

DB

SELECT /*water2.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND ((1=1)) FOR UPDATE

④ 如果checksum相同,說明主從資料一致,就繼續下一個chunk

⑤ 如果checksum不同,說明該chunk有不一致,深入chunk內部,逐行計算checksum並比較,如果發現某行不一致,則標記下來,繼續檢測剩餘行,直到這個chunk結束。

⑥ 直到修復該chunk所有不一致的行,繼續檢查和修復下一個chunk

⑵ checksum演算法

2個層次的校驗演算法,一是塊級,一是行級。

① 單行資料checksum值計算

  檢查表結構並獲取每一列的資料型別,把所有資料型別都轉化為字串,然後用concat_ws()函式進行拼接,由此計算出該行的checksum值,checksum預設採用crc32計算。下面是一個例子:

SELECT /*rows in chunk*/ `id`, `name`, CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS __crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND (1=1) ORDER BY `id` FOR UPDATE;

② 資料塊checksum值的計算

  智慧分析表上的索引,然後把表的資料split成若干個chunk,計算的時候以chunk為單位,可以理解為把chunk內的所有行的資料拼接起來,再計算crc32的值,即得到該chunkchecksum值。下面是一個例子:

SELECT /*water2.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND ((1=1)) FOR UPDATE; 

⑶ 資料精確切分

鎖生命週期是我們一直很注重的問題。

2010年底和2011年初,彼時我們剛剛引入了資料線上校驗方案不久,在企鵝gamedb每天日常checksum時,DB有鎖資料情況,導致TTC大量資料無法寫入的告警,mk-table-checksum 1.2.8 版本資料分片方法不合理,當表資料分佈非常不均勻時,資料切片會導致某些塊包含的資料行過大,其中innodb行鎖實現為索引間隙鎖,checksum過程會鎖住chunk的資料。

felixliang修改mk-table-checksum的原始碼,增加自定義recursive_dynamic_calculate_chunks函式,實現了精確資料切片控制,該函式內部呼叫explain檢視每個chunk包含的rows,確保每個chunk中的行數不多於chunk-size引數設定的大小。當每個chunk切分均勻後,chunk資料校驗在1秒左右完成,鎖資料情況幾乎很難感知得到。

這個方案已經在騰訊遊戲日常資料校驗穩定執行3年多,我們相信該切分演算法比官方預設的切分而言更加健壯、同時也更加安全。而pt-table-sync同樣會對不一致的表切分,由此我們遷移了該切分演算法到pt-table-sync裡面,這也是”前人栽樹,後人乘涼”的好處。

⑷ 延遲控制

這是為什麼我們要採用replicate模式的必要性。

replicate模式只是普通的執行緒請求行為,跳過MySQLReplication機制,本身不做延遲控制,然而,pt-table-sync在修復過程中是不能容忍從庫延遲,如果延遲太多,pt-table-sync會長期持有對chunkfor update鎖,然後等待從庫的master_pos_wait執行完畢或超時,從庫延遲越大,等待過程就越長,主庫加鎖的時間就越長,對線上影響就越大。但是如果不等待,這個工具是無法驗證主從資料是否一致。

但是,replicate模式下,補償SQL是通過master上執行,生成binlog,然後全量同步到slave,再在slave上回放,從而達到資料修復的效果。而資料安全是生命線,改錯了master就得回檔,就不是鬧著玩的。改錯了slave不會對玩家有影響,對DBA是個保護。

那麼,我們既想要replicate模式帶來的好處,又想避免補償SQLmaster執行帶來的風險不可控因素,該如何做?

互娛DBA團隊通過修改get_change_dbh函式讓最後一步生成的補償SQL不走binlog,直接在slave上跑,從而避免在master上修復帶來的資料安全問題。

⑸ 普通索引

pt-table-sync採用replace into來修復主從不一致,必須保證被replace的表有主鍵或唯一鍵,否則replace into退化成insert into,而insert是不能在master上執行,因為那樣只會使不一致擴散。

對發現主從不一致的行,採用replace into 語句,在主庫上執行一遍以生成該行的全量binlog,並同步到從庫,這會以主庫資料為基礎修復從庫:

① 對於主庫有的行而從庫沒有的行,採用replace在主庫上插入(必須不能是insert

② 對於從庫有的行而主庫沒有的行,通過在主庫執行delete來刪除

因為現網環境比較複雜,我們不能保證騰訊全球遊戲每個表上都有主鍵或唯一鍵。因此,互娛DBA團隊通過原始碼修改,當發現主庫上的表沒有唯一鍵時不會致命退出,而是繼續執行,但採用了另外一種演算法,即在slave上採用delete + replace 方式修補。

⑹ 平臺相容

我們先看mk-table-checksumpt-table-checksum表結構。

⒈ mk-table-checksum

 

. Pt-table-checksum

 

是的,這2個表結構是不一樣的,而pt-table-syncreplicate模式是直接讀取pt-table-checksum的表,但是我們:
① mk-table-checksum修復版已經整合到我們的GCS平臺

② 保留pt-table-sync最新版本的功能,避免引入mk-table-sync老版本bug

基於上述2個理由,我們修改了find_replication_differences函式,讓pt-table-sync相容了mk-table-checksum,這樣既能相容現有平臺的功能,又可以用得上pt-table-sync最新版本的新特性。

⑺ 超時控制

在我們測試過程中,發現官方提供的超時控制--wait引數有”bug”

① 對於非replicate模式,wait引數無效

② 對於replicate模式,wait只能是0和非0,當非0時,任何值都是一樣的

下面是我們的測試現象

 

所以,無論哪種模式,wait引數都是沒有用的。

我們修改原始碼來通過外部引數的動態控制超時行為:

 

5. 小結

資料自愈是資料校驗的一種延續與補充,是隨著後續業務全量鋪開而互娛DBA團隊不斷定製開發演變的資料修復服務方案。在資料自愈的服務化之路上,相信我們會越來越好。

http://blog.csdn.net/dba_waterbin/article/details/43608587

騰訊遊戲資料自愈服務方案簡介

1. 引言

在正式介紹專案背景之前,讓我們先看一組資料:

 

這是2個灰度的業務,都是Z3伺服器,我們先只從時間成本的收益角度來看:

⑴  左一業務資料量是330G,資料不一致時通過重做slave需要150分鐘左右,而藉助pt-table-sync只需要5分鐘,速度提升30 倍。

⑵  右一業務的量是93G,通過sync工具花費3分鐘,而如果重做slave35分鐘,速度提升12倍。

引入這組資料意在指明,整個過程不僅解放了DBA的雙手,也符合”零運維”的趨勢,資料自愈將是互娛DBA團隊在未來提供的服務之一。

2. 背景

MySQL資料庫基於binlog的資料同步方案,絕大部分情況下能保證主備資料完全一致,但某些異常情況下,例如開發使用了unsafe statementSQL(如帶limit)及硬體故障等,都可能導致主備資料不一致。DBA通過checksum工具可以發現這些不一致的情況,但往往需要重新做一個熱備來恢復主備資料一致性,並且此過程可能需要10小時以上,而實際情況上主備資料通常僅有少量不一致,線上修復這些資料差異可以更高效地完成一個數據一致的熱備。

3. 收益

重做熱備是我們目前首選的修復方案,但有時候沒有備用的新機子卻讓修復步伐戛然而止,而如果沒有修復,倘若master故障,由於資料不一致,切換到slave是存在資料丟失的風險,那麼又不得不執行修復,DBA就需要評估以前slave上的連線切換到master是否會影響master的效能......這樣DBA的工作量就無形中翻倍了。

我們在引言中也道出了sync工具相比傳統的熱備在時間上的收益,但除了這個,資料自愈服務的收益包括但不限於:

▼ 業務資料更安全,恢復熱備時間變短

▼ 減少伺服器資源,避免重做熱備的機器申請

▼ 提升DBA做熱備的處理效率

▼ 降低溝通成本,保證業務持續穩定執行

4. 資料自愈解決方案

我們從開源社群引入了Percona公司的pt-table-sync,該專案從2007年啟動。

下面我們對該工具的實現細節以及互娛DBA團隊在此基礎上進行定製開發的部分內容進行討論。

⑴  流程圖

 

Pt-table-sync2種修復模式:replicate模式和非replicate模式,上圖是replicate的,這也是我們所推薦,原因會在下文說明。

這裡,我們先對上圖作下簡要介紹

① 對每一個chunk,再校驗時加上for update鎖,一旦獲得鎖,就記錄下當前主庫的show master status值。以我測試機的案例:

SELECT /*water2.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND ((1=1)) FOR UPDATE

SHOW MASTER STATUS

② 在從庫上執行select master_pos_wait()函式,等待從庫SQL執行緒執行到show master status得到的位置,以此保證,主從上關於這個chunk的內容均不再改變。

SELECT MASTER_POS_WAIT('binlog3306.000014', 139672350, 60)

③ 對這個chunk執行checksum,然後與主庫的checksum進行比較

DR

SELECT /*water2.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND ((1=1)) LOCK IN SHARE MODE

DB

SELECT /*water2.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND ((1=1)) FOR UPDATE

④ 如果checksum相同,說明主從資料一致,就繼續下一個chunk

⑤ 如果checksum不同,說明該chunk有不一致,深入chunk內部,逐行計算checksum並比較,如果發現某行不一致,則標記下來,繼續檢測剩餘行,直到這個chunk結束。

⑥ 直到修復該chunk所有不一致的行,繼續檢查和修復下一個chunk

⑵ checksum演算法

2個層次的校驗演算法,一是塊級,一是行級。

① 單行資料checksum值計算

  檢查表結構並獲取每一列的資料型別,把所有資料型別都轉化為字串,然後用concat_ws()函式進行拼接,由此計算出該行的checksum值,checksum預設採用crc32計算。下面是一個例子:

SELECT /*rows in chunk*/ `id`, `name`, CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS __crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND (1=1) ORDER BY `id` FOR UPDATE;

② 資料塊checksum值的計算

  智慧分析表上的索引,然後把表的資料split成若干個chunk,計算的時候以chunk為單位,可以理解為把chunk內的所有行的資料拼接起來,再計算crc32的值,即得到該chunkchecksum值。下面是一個例子:

SELECT /*water2.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `name`, CONCAT(ISNULL(`name`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `water2`.`t` FORCE INDEX (`PRIMARY`) WHERE (1=1) AND ((1=1)) FOR UPDATE; 

⑶ 資料精確切分

鎖生命週期是我們一直很注重的問題。

2010年底和2011年初,彼時我們剛剛引入了資料線上校驗方案不久,在企鵝gamedb每天日常checksum時,DB有鎖資料情況,導致TTC大量資料無法寫入的告警,mk-table-checksum 1.2.8 版本資料分片方法不合理,當表資料分佈非常不均勻時,資料切片會導致某些塊包含的資料行過大,其中innodb行鎖實現為索引間隙鎖,checksum過程會鎖住chunk的資料。

felixliang修改mk-table-checksum的原始碼,增加自定義recursive_dynamic_calculate_chunks函式,實現了精確資料切片控制,該函式內部呼叫explain檢視每個chunk包含的rows,確保每個chunk中的行數不多於chunk-size引數設定的大小。當每個chunk切分均勻後,chunk資料校驗在1秒左右完成,鎖資料情況幾乎很難感知得到。

這個方案已經在騰訊遊戲日常資料校驗穩定執行3年多,我們相信該切分演算法比官方預設的切分而言更加健壯、同時也更加安全。而pt-table-sync同樣會對不一致的表切分,由此我們遷移了該切分演算法到pt-table-sync裡面,這也是”前人栽樹,後人乘涼”的好處。

⑷ 延遲控制

這是為什麼我們要採用replicate模式的必要性。

replicate模式只是普通的執行緒請求行為,跳過MySQLReplication機制,本身不做延遲控制,然而,pt-table-sync在修復過程中是不能容忍從庫延遲,如果延遲太多,pt-table-sync會長期持有對chunkfor update鎖,然後等待從庫的master_pos_wait執行完畢或超時,從庫延遲越大,等待過程就越長,主庫加鎖的時間就越長,對線上影響就越大。但是如果不等待,這個工具是無法驗證主從資料是否一致。

但是,replicate模式下,補償SQL是通過master上執行,生成binlog,然後全量同步到slave,再在slave上回放,從而達到資料修復的效果。而資料安全是生命線,改錯了master就得回檔,就不是鬧著玩的。改錯了slave不會對玩家有影響,對DBA是個保護。

那麼,我們既想要replicate模式帶來的好處,又想避免補償SQLmaster執行帶來的風險不可控因素,該如何做?

互娛DBA團隊通過修改get_change_dbh函式讓最後一步生成的補償SQL不走binlog,直接在slave上跑,從而避免在master上修復帶來的資料安全問題。

⑸ 普通索引

pt-table-sync採用replace into來修復主從不一致,必須保證被replace的表有主鍵或唯一鍵,否則replace into退化成insert into,而insert是不能在master上執行,因為那樣只會使不一致擴散。

對發現主從不一致的行,採用replace into 語句,在主庫上執行一遍以生成該行的全量binlog,並同步到從庫,這會以主庫資料為基礎修復從庫:

① 對於主庫有的行而從庫沒有的行,採用replace在主庫上插入(必須不能是insert

② 對於從庫有的行而主庫沒有的行,通過在主庫執行delete來刪除

因為現網環境比較複雜,我們不能保證騰訊全球遊戲每個表上都有主鍵或唯一鍵。因此,互娛DBA團隊通過原始碼修改,當發現主庫上的表沒有唯一鍵時不會致命退出,而是繼續執行,但採用了另外一種演算法,即在slave上採用delete + replace 方式修補。

⑹ 平臺相容

我們先看mk-table-checksumpt-table-checksum表結構。

⒈ mk-table-checksum

 

. Pt-table-checksum

 

是的,這2個表結構是不一樣的,而pt-table-syncreplicate模式是直接讀取pt-table-checksum的表,但是我們:
① mk-table-checksum修復版已經整合到我們的GCS平臺

② 保留pt-table-sync最新版本的功能,避免引入mk-table-sync老版本bug

基於上述2個理由,我們修改了find_replication_differences函式,讓pt-table-sync相容了mk-table-checksum,這樣既能相容現有平臺的功能,又可以用得上pt-table-sync最新版本的新特性。

⑺ 超時控制

在我們測試過程中,發現官方提供的超時控制--wait引數有”bug”

① 對於非replicate模式,wait引數無效

② 對於replicate模式,wait只能是0和非0,當非0時,任何值都是一樣的

下面是我們的測試現象