MySQL主從復(fù)制從庫(kù)IO線程源碼分析

mysql主從復(fù)制通過binlog來同步數(shù)據(jù),在從庫(kù)上執(zhí)行start slave,會(huì)開啟兩個(gè)線程,分別是io線程和sql線程。io線程負(fù)責(zé)從主庫(kù)拉取binlog并存儲(chǔ)到本地的relay log,sql線程負(fù)責(zé)把relay log中的事務(wù)在從庫(kù)應(yīng)用。本文將結(jié)合源碼分析io線程的具體實(shí)現(xiàn)。

MySQL源碼版本:5.7.19

原文地址:
https://mytecdb.com/blogDetail.php?id=85

1. io線程函數(shù)

mysql io線程函數(shù)在源碼sql/rpl_slave.cc中實(shí)現(xiàn),執(zhí)行命令start slave或者start slave io_thread,就會(huì)創(chuàng)建io線程,io線程函數(shù)為:
extern "C" void *handle_slave_io(void *arg)

io線程函數(shù)的參數(shù)arg,實(shí)際上是一個(gè)Master_info類型的指針mi,保存了主庫(kù)相關(guān)的一些信息,比如host,user,password,主庫(kù)uuid,主庫(kù)binlog文件名,主庫(kù)binlog文件位置等等。

io線程函數(shù)中會(huì)創(chuàng)建一個(gè)THD的對(duì)象,一個(gè)THD的對(duì)象就相當(dāng)于一個(gè)連接,這個(gè)連接是可以在show processlist中看到,實(shí)際上io線程也可以理解為一個(gè)通向主庫(kù)的連接。

緊接著io線程函數(shù)初始化一個(gè)MYSQL結(jié)構(gòu)體的指針,稱之為mysql,主要作用是與主庫(kù)通信,走的標(biāo)準(zhǔn)的MySQL客戶端/服務(wù)器協(xié)議,后面io線程獲取主庫(kù)的信息,注冊(cè)從庫(kù),拉取binlog,都將依賴這個(gè)指針。

2. 創(chuàng)建連接

有了mi和mysql,通過調(diào)用函數(shù)safe_connect(thd, mysql, mi),連接到主庫(kù)。

3. 交換信息

調(diào)用get_master_version_and_clock(mysql, mi),獲取主庫(kù)的版本,時(shí)間,server_id,字符集校驗(yàn)規(guī)則,時(shí)區(qū)等等信息,如果發(fā)現(xiàn)主庫(kù)的server_id與從庫(kù)的server_id相同,則會(huì)報(bào)錯(cuò)。

調(diào)用get_master_uuid(mysql, mi),獲取主庫(kù)的server_uuid,如果主庫(kù)的server_uuid與從庫(kù)的server_uuid相同,也會(huì)報(bào)錯(cuò)。

調(diào)用io_thread_init_commands(mysql, mi),把從庫(kù)的server_uuid作為session變量設(shè)置到連接上,發(fā)送給主庫(kù)。

4. 注冊(cè)從庫(kù)

調(diào)用 register_slave_on_master(mysql, mi, &suppress_warnings),在主庫(kù)上注冊(cè)從庫(kù),注冊(cè)過程實(shí)際上就是發(fā)送一個(gè)注冊(cè)命令COM_REGISTER_SLAVE,以及一個(gè)特定結(jié)構(gòu)的網(wǎng)絡(luò)包到主庫(kù)。這個(gè)包結(jié)構(gòu)如下:

  • 從庫(kù)server_id,4字節(jié)
  • 從庫(kù)report_host,如果為空,占用1個(gè)字節(jié),值為0
  • 從庫(kù)report_user,如果為空,占用1個(gè)字節(jié),值為0
  • 從庫(kù)report_password,如果為空,占用1個(gè)字節(jié),值為0
  • 從庫(kù)report_port,2字節(jié)
  • rpl_recovery_rank,4字節(jié),默認(rèn)為0,兼容老版本
  • master_id,4字節(jié),值為0,由主庫(kù)來填充

這些結(jié)構(gòu)通過抓包,可以清楚地看到包內(nèi)容與上述結(jié)構(gòu)完全吻合。

5. 拉取binlog

調(diào)用request_dump(thd, mysql, mi, &suppress_warnings),向主庫(kù)發(fā)送binlog拉取的命令。如果開啟GTID,命令為COM_BINLOG_DUMP_GTID,如果沒有開啟GTID,命令為COM_BINLOG_DUMP。

請(qǐng)求拉取binlog的網(wǎng)絡(luò)包結(jié)構(gòu)為:

  • binlog_flags,2字節(jié)
  • 從庫(kù)server_id,4字節(jié)
  • binlog名稱的大小,4字節(jié)
  • binlog名稱,占用字節(jié)由3決定
  • binlog位置,8字節(jié)
  • binlog數(shù)據(jù)大小,4字節(jié)
  • gtid_executed的大小,4字節(jié)
  • uuid的數(shù)量,8字節(jié)
  • uuid1,16字節(jié)
  • gtid段數(shù)量,8字節(jié)
  • gtid1 start,8字節(jié)
  • gtid1 end,8字節(jié)
  • gtid2 start,8字節(jié)(可能沒有)
  • gtid2 end,8字節(jié)(可能沒有)
  • uuid2....等等(可能沒有)

開啟GTID之后,binlog文件名和binlog位置這兩個(gè)參數(shù)都是固定值,binlog文件名全為0,binlog位置為4,gtid_executed才是真正起作用的。

6. 讀取binlog

請(qǐng)求拉取binlog的命令發(fā)送完成之后,io線程在一個(gè)while循環(huán)中,不斷調(diào)用read_event(mysql, mi, &suppress_warnings) 來讀取主庫(kù)發(fā)送的binlog數(shù)據(jù),并寫入到relay log文件中。

7. 總結(jié)

至此io線程的整體邏輯就完成了,從上述過程來看,io線程連接到主庫(kù),與主庫(kù)交換一些必要的信息,在主庫(kù)上注冊(cè)從庫(kù),然后請(qǐng)求拉取binlog,最后循環(huán)調(diào)用read_event,將主庫(kù)不斷發(fā)送的binlog信息寫入到relay log文件中。整個(gè)過程很簡(jiǎn)單,但是需要考慮的細(xì)節(jié)有很多。了解io線程的主要流程,我們甚至可以自己寫一個(gè)程序,模擬io線程,從主庫(kù)拉取binlog。當(dāng)然前提是需要了解MySQL的client/server的協(xié)議,或者使用現(xiàn)成的mysql開發(fā)庫(kù)。


附錄:

  • io線程函數(shù)handle_slave_io定義:sql/rpl_slave.cc
  • Master_info類定義:sql/rpl_mi.h
  • MYSQL結(jié)構(gòu)體定義:include/mysql.h
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容