在之前的文章《mysql主從復(fù)制io線程源碼分析》弥姻,我們分析了MySQL從庫(kù)的io線程工作的主要過程缅疟,大致回顧一下筹煮,如下:
- 連接主庫(kù)
- 發(fā)送COM_REGISTER_SLAVE命令注冊(cè)從庫(kù)
- 發(fā)送COM_BINLOG_DUMP_GTID命令請(qǐng)求拉取binlog
下面將結(jié)合源碼助泽,分析一下主庫(kù)接收到從庫(kù)io線程發(fā)送過來(lái)的命令后采郎,是如何具體處理的懂昂。
MySQL源碼版本:5.7.19
原文地址:
https://mytecdb.com/blogDetail.php?id=86
1. 注冊(cè)從庫(kù)
主庫(kù)在接收到從庫(kù)發(fā)送的COM_REGISTER_SLAVE命令后介时,會(huì)調(diào)用register_slave函數(shù)完成從庫(kù)的注冊(cè)。主要的執(zhí)行邏輯如下:
獲取從庫(kù)發(fā)送的與注冊(cè)相關(guān)的一些數(shù)據(jù)凌彬,包括從庫(kù)的server_id沸柔,report_host,report_user铲敛,report_password褐澎,port等等,這些數(shù)據(jù)會(huì)被存放在一個(gè)叫做SLAVE_INFO的結(jié)構(gòu)里伐蒋,這個(gè)結(jié)構(gòu)最終會(huì)被放到slave_list變量中工三,slave_list的類型是一個(gè)HASH表。slave_list是一個(gè)全局變量先鱼,位于源碼文件 sql/rpl_master.cc俭正,這個(gè)變量在用戶執(zhí)行show slave hosts時(shí)也會(huì)用到。
如果slave_list這個(gè)HASH表中已經(jīng)有從庫(kù)的信息了焙畔,也就是包含相同server_id的從庫(kù)信息已經(jīng)存在掸读,那么會(huì)先刪掉該從庫(kù)信息,再把新的加進(jìn)去。
2. 發(fā)送binlog
主庫(kù)在接收到從庫(kù)發(fā)送的COM_BINLOG_DUMP_GTID命令后儿惫,會(huì)調(diào)用com_binlog_dump_gtid函數(shù)處理從庫(kù)拉取binlog的請(qǐng)求澡罚。主要的執(zhí)行邏輯如下:
- 獲取從庫(kù)發(fā)送的binlog相關(guān)信息,包括server_id肾请,binlog名稱留搔,binlog位置,binlog大小铛铁,gtid信息等等隔显。
- 檢查是否已經(jīng)存在與該從庫(kù)關(guān)聯(lián)的binlog dump線程,如果存在避归,結(jié)束該binlog dump線程荣月。為什么會(huì)已經(jīng)存在binlog dump線程?在某些場(chǎng)景下梳毙,比如從庫(kù)io線程停止,這時(shí)主庫(kù)的binlog dump線程正好在等待binlog更新捐下,即等待主庫(kù)寫入數(shù)據(jù)账锹,如果主庫(kù)一直沒有寫入數(shù)據(jù),dump線程就會(huì)等待很長(zhǎng)時(shí)間坷襟,如果這時(shí)從庫(kù)io線程重連到主庫(kù)奸柬,就會(huì)發(fā)現(xiàn)主庫(kù)已經(jīng)存在與該從庫(kù)對(duì)應(yīng)的dump線程。所以主庫(kù)在處理從庫(kù)binlog dump請(qǐng)求時(shí)婴程,先檢查是否已經(jīng)存在dump線程廓奕。
- 調(diào)用mysql_binlog_send函數(shù),向從庫(kù)發(fā)送binlog档叔。這個(gè)函數(shù)內(nèi)部實(shí)際是通過一個(gè)C++類Binlog_sender來(lái)實(shí)現(xiàn)的桌粉,該類在源碼文件sql/rpl_binlog_sender.h中定義,調(diào)用該類的run成員函數(shù)來(lái)發(fā)送binlog衙四。
- Binlog_sender類的run成員函數(shù)铃肯,主要邏輯是通過多個(gè)while嵌套循環(huán),依次讀取binlog文件传蹈,binlog文件中的event押逼,將event發(fā)送給從庫(kù)。如果event已經(jīng)在從庫(kù)中應(yīng)用惦界,則忽略該event挑格。當(dāng)讀到最新的binlog時(shí),如果所有event都已經(jīng)發(fā)送完成沾歪,那么線程會(huì)等待binlog更新事件update_cond漂彤,有新的binlog event寫入后,會(huì)廣播通知所有等待update_cond事件的線程開始工作,也包括dump線程显歧。dump線程在等待update_cond事件時(shí)有一個(gè)超時(shí)時(shí)間仪或,這個(gè)時(shí)間就是master_heartbeat_period,即主庫(kù)dump線程與從庫(kù)io線程的心跳時(shí)間間隔士骤,這個(gè)值在從庫(kù)執(zhí)行change master 時(shí)設(shè)置范删,啟動(dòng)io線程時(shí)把該值傳遞給主庫(kù),主庫(kù)dump線程等待update_cond超時(shí)后拷肌,將會(huì)給從庫(kù)發(fā)送一個(gè)heartbeat event到旦,之后會(huì)繼續(xù)等待update_cond事件。上述過程會(huì)一直循環(huán)巨缘,直到dump線程被kill或者遇到其他錯(cuò)誤添忘。
- 當(dāng)執(zhí)行邏輯從Binlog_sender類內(nèi)部的while循環(huán)退出,緊接著會(huì)調(diào)用unregister_slave函數(shù)注銷從庫(kù)的注冊(cè)若锁。這個(gè)時(shí)候在主庫(kù)上執(zhí)行show slave hosts搁骑,就會(huì)發(fā)現(xiàn)從庫(kù)的信息已經(jīng)沒有了。
3. 思考一個(gè)問題
從庫(kù)執(zhí)行stop slave又固,我們立即在主庫(kù)上執(zhí)行show slave hosts仲器,發(fā)現(xiàn)從庫(kù)信息仍然存在,過一段時(shí)間仰冠,大概60秒左右乏冀,再次執(zhí)行,才發(fā)現(xiàn)從庫(kù)信息消失洋只,這是為什么辆沦?
從庫(kù)執(zhí)行stop slave,只是將io線程結(jié)束掉识虚,并不會(huì)通知主庫(kù)dump線程肢扯,主庫(kù)dump線程在給從庫(kù)發(fā)送binlog event或者心跳包時(shí),由于從庫(kù)io線程已經(jīng)結(jié)束舷礼,網(wǎng)絡(luò)包無(wú)響應(yīng)鹃彻,主庫(kù)等待net.ipv4.tcp_fin_timeout(默認(rèn)60秒)后,報(bào)異常妻献,退出Binlog_sender內(nèi)部的while循環(huán)蛛株,調(diào)用unregister_slave函數(shù)注銷從庫(kù)的注冊(cè),此時(shí)再次在主庫(kù)執(zhí)行show slave hosts育拨,就不會(huì)再看到從庫(kù)的信息了谨履。
附錄:
主要源碼文件:
sql/rpl_master.h
sql/rpl_master.cc
sql/rpl_binlog_sender.h
sql/rpl_binlog_sender.cc