MySQL的Binlog與主從復(fù)制


在MySQL中,可以使用多種存儲(chǔ)引擎。其中最常用的InnoDB引擎支持事務(wù),Redo Log和Undo Log就是InnoDB里面的工具,用于實(shí)現(xiàn)事務(wù)。而Binlog是MySQL層面的東西,用于實(shí)現(xiàn)主從復(fù)制,與使用的存儲(chǔ)引擎無(wú)關(guān)。

通過(guò)監(jiān)聽(tīng)并解析Mater的Binlog,也可以實(shí)現(xiàn)將MySQL中的數(shù)據(jù)同步到其他應(yīng)用組件中(比如更新緩存)的效果。

在不發(fā)生宕機(jī)的情況下,未提交的事務(wù)和已回滾的事務(wù)是不寫入Binlog日志中的,只有提交成功的事務(wù)才寫入Binlog日志。這一點(diǎn)和Redo Log不一樣,Redo Log中會(huì)記錄未提交、已回滾的事務(wù)內(nèi)容。

Binlog是一種邏輯日志——例如Binlog的statement格式記錄原始SQL語(yǔ)句、RAW格式記錄某一行修改前后的值——且一個(gè)事務(wù)的日志在Binlog中是連續(xù)排列的,因此要求每個(gè)事務(wù)都要串行地寫入,這意味著每個(gè)事務(wù)在寫B(tài)inlog之前都要排他地鎖住Binlog,這會(huì)導(dǎo)致寫的效率很低。MySQL5.6之后,通過(guò)pipline技術(shù)異步地批量化將已提交的事務(wù)內(nèi)容寫入Binlog。

一個(gè)事務(wù)的提交既要寫B(tài)inlog日志又要寫Redo Log日志,如何保證雙寫的原子性?一個(gè)寫成功,寫另外一個(gè)時(shí)發(fā)生宕機(jī),重啟后如何處理?在討論這個(gè)問(wèn)題之前,先說(shuō)下Binlog自身寫入的原子性問(wèn)題:Binlog刷盤到一半,出現(xiàn)宕機(jī),這個(gè)問(wèn)題和Redo Log的寫入原子性是同樣的問(wèn)題,通過(guò)類似于checksum的辦法或者Binlog中的結(jié)束標(biāo)記來(lái)判斷出某個(gè)事務(wù)的Binlog這是不是不完整的Binlog,從而把不完整的部分截掉。對(duì)于客戶端來(lái)說(shuō),此時(shí)宕機(jī),事務(wù)肯定是沒(méi)有提交成功的,所以截掉也沒(méi)問(wèn)題。下面來(lái)講如何保證雙寫B(tài)inlog和Redo Log的原子性。由于雙寫B(tài)inlog和Redo Log發(fā)生在同一臺(tái)機(jī)器上,這其實(shí)是一個(gè)內(nèi)部分布式事務(wù),可以使用兩階段提交法來(lái)實(shí)現(xiàn)雙寫的原子性。簡(jiǎn)單來(lái)說(shuō)就是:

1)第一階段(準(zhǔn)備階段):MySQL Server要求innoDB完成將事務(wù)內(nèi)容寫入Redo Log中的工作,只等事務(wù)提交;以及,MySQL Server完成Binlog內(nèi)容寫入內(nèi)存的工作,只等刷盤。兩個(gè)都準(zhǔn)備好之后,會(huì)向MySQL Server發(fā)送OK反饋,MySQL Server緊接著執(zhí)行第二階段。

2)第二階段(提交階段):收到客戶端的Commit指令,MySQL Server先將內(nèi)存中的Binlog刷盤,然后讓innoDB執(zhí)行事務(wù)的提交。兩個(gè)都完成之后,會(huì)向MySQL Server發(fā)送OK反饋,兩階段提交結(jié)束。

若雙寫B(tài)inlog和Redo Log的過(guò)程中發(fā)生宕機(jī),處理思路為:

1)若宕機(jī)發(fā)生在第一階段,此時(shí)Binlog還在內(nèi)存中,宕機(jī)導(dǎo)致全部消失。而Redo Log記錄了未提交的日志,MySQL Server重啟后感知到Binlog中不存在Redo Log中記錄的未提交事務(wù),會(huì)自行回滾未提交事務(wù)的Redo Log日志;

2)若宕機(jī)發(fā)生在第二階段,Binlog寫了一半,innoDB還未執(zhí)行提交,MySQL Server重啟后會(huì)對(duì)Binlog做截?cái)?,?duì)Redo Log中記錄的未提交事務(wù)做回滾;

3)若宕機(jī)發(fā)生在第二階段,Binlog寫入成功,innoDB還未執(zhí)行提交,MySQL Server重啟后會(huì)通過(guò)checksum的辦法或者Binlog中的結(jié)束標(biāo)記感知到Binlog寫入成功,緊接著對(duì)Binlog中存在的、但Redo Log未提交的事務(wù)發(fā)起提交。


在MySQL的Master / Slave集群模式中,有三種主從復(fù)制模式:

1)同步復(fù)制:所有的Slave都收到Master發(fā)送的Binlog,并且接收完,Master才認(rèn)為事務(wù)提交成功,再對(duì)客戶端返回成功。這種方式最安全,但是性能很差;

2)異步復(fù)制:只要Master事務(wù)提交成功,就對(duì)客戶端返回成功。后臺(tái)線程異步地將Binlog發(fā)送給Slave,然后Slave回放Binlog。這種方式性能最好,但是可能會(huì)導(dǎo)致數(shù)據(jù)丟失;

3)半同步復(fù)制:Master事務(wù)提交后,同時(shí)把Binlog同步給Slave,只要有部分(數(shù)量可以配置)Slave收到了Binlog,就認(rèn)為事務(wù)提交成功,對(duì)客戶端返回。

對(duì)于半異步復(fù)制,如果Slave超時(shí)后還未返回,也會(huì)退化為異步復(fù)制。所以無(wú)論是異步復(fù)制還是半異步復(fù)制,都無(wú)法嚴(yán)格保證主從中的數(shù)據(jù)完全一致,主從復(fù)制的延遲會(huì)導(dǎo)致主節(jié)點(diǎn)宕機(jī)后部分?jǐn)?shù)據(jù)未來(lái)得及同步到從節(jié)點(diǎn),從而丟失數(shù)據(jù)。但是主節(jié)點(diǎn)宕機(jī)后,還是要立即切換到從節(jié)點(diǎn),保證服務(wù)的可用(犧牲一致性保證可用性),數(shù)據(jù)的丟失可以通過(guò)后續(xù)的人工干預(yù)來(lái)補(bǔ)償。


要保證MySQL數(shù)據(jù)不丟失,replication是一個(gè)很好的解決方案,而MySQL也提供了一套強(qiáng)大的replication機(jī)制。只是我們需要知道,為了性能考量,replication是采用的asynchronous模式,也就是寫入的數(shù)據(jù)并不會(huì)同步更新到slave上面,如果這時(shí)候master當(dāng)機(jī),我們?nèi)匀豢赡軙?huì)面臨數(shù)據(jù)丟失的風(fēng)險(xiǎn)。

為了解決這個(gè)問(wèn)題,我們可以使用semi-synchronous replication,semi-synchronous replication的原理很簡(jiǎn)單,當(dāng)master處理完一個(gè)事務(wù),它會(huì)等待至少一個(gè)支持semi-synchronous的slave確認(rèn)收到了該事件并將其寫入relay-log之后,才會(huì)返回。這樣即使master當(dāng)機(jī),最少也有一個(gè)slave獲取到了完整的數(shù)據(jù)。

但是,semi-synchronous并不是100%的保證數(shù)據(jù)不會(huì)丟失,如果master在完成事務(wù)并將其發(fā)送給slave的時(shí)候崩潰,仍然可能造成數(shù)據(jù)丟失。只是相比于傳統(tǒng)的異步復(fù)制,semi-synchronous replication能極大地提升數(shù)據(jù)安全。更為重要的是,它并不慢,MHA的作者都說(shuō)他們?cè)趂acebook的生產(chǎn)環(huán)境中使用了semi-synchronous(這里),所以我覺(jué)得真心沒(méi)必要擔(dān)心它的性能問(wèn)題,除非你的業(yè)務(wù)量級(jí)已經(jīng)完全超越了facebook或者google。在這篇文章里面已經(jīng)提到,MySQL 5.7之后已經(jīng)使用了Loss-Less Semi-Synchronous replication,所以丟數(shù)據(jù)的概率已經(jīng)很小了。

如果真的想完全保證數(shù)據(jù)不會(huì)丟失,現(xiàn)階段一個(gè)比較好的辦法就是使用gelera,一個(gè)MySQL集群解決方案,它通過(guò)同時(shí)寫三份的策略來(lái)保證數(shù)據(jù)不會(huì)丟失。筆者沒(méi)有任何使用gelera的經(jīng)驗(yàn),只是知道業(yè)界已經(jīng)有公司將其用于生產(chǎn)環(huán)境中,性能應(yīng)該也不是問(wèn)題。但gelera對(duì)MySQL代碼侵入性較強(qiáng),可能對(duì)某些有代碼潔癖的同學(xué)來(lái)說(shuō)不合適了。

我們還可以使用drbd來(lái)實(shí)現(xiàn)MySQL數(shù)據(jù)復(fù)制,MySQL官方文檔有一篇文檔有詳細(xì)介紹,但筆者并未采用這套方案,MHA的作者寫了一些采用drdb的問(wèn)題,在這里,僅供參考。


?著作權(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ù)。

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