複製錯誤案例分享(二)

沃趣科技發表於2018-11-12

上期《 複製錯誤案例分享(一) 》為大家分享了兩個案例,本期繼續為大家分享案例。

本期的案例中,雖然是5.5及以前版本的MySQL複製才會出現的問題,但是現在不少公司的老系統用的就是5.5甚至更古老的5.1或者5.0的資料庫。有時候面對這些老古董的時候,不瞭解這些舊版資料庫的特性的話,那就是自己往坑裡跳。


| 案例三:server_id引起的複製錯誤

環境資訊

  • 主庫 IP:192.168.1.130 server_id:3656

  • 從庫A IP:192.168.1.36 server_id:56

  • 從庫B IP:192.168.1.57 server_id:56

5.5.36版本現象

初始搭建環境之後,檢視各主機狀態。搭建環境的步驟就省略。

主庫(192.168.1.130)

主庫通過show processlist語句檢視,只有一個dump執行緒,但是通過多次重新整理,可以看到連線的是不同的伺服器。可以看到每次通過show processlist語句顯示的dump執行緒的Host欄位中,IP:PORT的值是不斷在更新的,說明dump執行緒在不斷的重連,才會出現佔用不同的埠的現象。

從庫A(192.168.1.36)

通過  show slave status\G 命令檢視複製狀態,多次執行可以看到  Slave_IO_Running 欄位顯示的內容,出現YES或者Connnecting兩種狀態。可以看到I/O執行緒在不斷的進行重連。 並且通過  tail-f 命令檢視error log,可以看到I/O執行緒一直在嘗試重新連線。

可以看到在錯誤日誌中列印的資訊是,I/O執行緒連線

從庫B(192.168.1.57)

從庫B現象與從庫A一致。

5.6.36版本現象

搭建環境步驟省略。

主庫(192.168.1.130)

show processlist檢視有兩個dump執行緒,並且多次重新整理,發現Host欄位中的IP:PORT並沒有修改,說明dump執行緒一直保持連線。

從庫A(192.168.1.36)

tail-f/home/mysql/data/mysqldata5.6/log/error.log 檢視錯誤日誌,沒有不斷斷開連線

從庫B(192.168.1.57)

tail -f /home/mysql/data/mysqldata5.6/log/error.log檢視錯誤日誌,沒有不斷斷開連線

原因分析

http://www.penglixun.com/tech/database/mysqlmultislavesameserverid.html這是彭立勳寫的關於多個slave使用相同serverid時衝突的原因的一篇文章。按照彭大大的分析,我理解的是,slave的I/O執行緒連線上主庫的時候,主庫上會呼叫  register_slave() 這個函式,在這個函式中又呼叫了unregisterslave()函式,會將之前使用相同serverid的執行緒給登出掉。從而導致從庫的I/O執行緒不斷斷開重連。 

但是仔細看了一下  unregister_slave() 函式的程式碼,並沒有發現MySQL是根據serverid來登出dump執行緒的。並且進一步比較了一下5.5.36和5.6.36版本的程式碼,並沒有發現不同。而從庫設定serverid一致導致I/O執行緒不斷重連的現象只在5.5版本中看到,在5.6版本中並沒有這個現象,所以導致5.5現象的原因不是在unregisterslave()函式中。 

進一步看了一下彭大大的文章,發現有人在下面評論,說主要是  kill_zombie_slave_threads() 函式導致的。於是看了一下  kill_zombie_slave_threads() 函式的邏輯,發現MySQL應該就是在這一步根據server_id將執行緒kill了。

  • 5.5.36版本 首先來看下5.5.36版本的  kill_zombie_dump_threads() 函式的程式碼。看到這個函式傳入的引數是一個uint32型別的slaveserverid,在函式中做的事情是,遍歷MySQL中的所有執行緒,如果遍歷到一個執行緒是dump執行緒並且執行緒的server_id是等於傳入的引數值話,則跳出遍歷迴圈,並kill掉這個執行緒。

  • 5.6.35版本 再來看一下5.6.36版本的  kill_zombie_dump_threads() 函式的程式碼實現,與5.5.36大不相同。首先傳入的引數是一THD型別的指標,在函式中實現的邏輯同樣是遍歷MySQL中的所有執行緒,如果找到dump執行緒,首先看一下這個執行緒有沒有uuid欄位(因為uuid是在5.6之後的版本才有的,這邊是為了相容5.5),如果有uuid則用uuid進行比較,如果沒有uuid,則用server_id進行比較。

  • 函式呼叫 知道了  kill_zombie_dump_threads() 執行緒實現的邏輯,那MySQL是在什麼地方會呼叫這個函式的呢。看了一下函式是在  caseCOM_BINLOG_DUMP 中被呼叫的。 在5.5.36版本中是在:

在5.6.36版本中也是在  caseCOM_BINLOG_DUMP 中,只不過是將之前的邏輯封裝在了  com_binlog_dump() 函式中了,  kill_zombie_dump_threads() 也是在  com_binlog_dump() 函式中呼叫的。

caseCOM_BINLOG_DUMP 中所進行的操作就是將dump執行緒通知I/O執行緒拉取新的binlog。

總結

整理下來的話,基本上可以確定主要是因為  kill_zombie_dump_threads() 函式導致在5.6之前的版本中,如果是一主多從的架構中,如果在從庫之間的serverid如果設定為一樣,會出現從開I/O執行緒不斷斷開重連的現象。因為在5.6之前的版本中,還沒有UUID的概念,MySQL使用serverid來區分是否是同一臺機器,而在5.6之後的版本是使用的UUID來區分。 總結一句,就是資料庫之間的server_id不要設定成一樣,不然可能會有一些不可預知的錯誤。


| 作者簡介

沈 剛·沃趣科技資料庫技術專家

熟悉MySQL資料庫執行機制,豐富的資料庫及複製架構故障診斷、效能調優、資料庫備份恢復及遷移經驗。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28218939/viewspace-2219571/,如需轉載,請註明出處,否則將追究法律責任。

相關文章