書名: MySQL高可用實踐作者名: 王雪迎本章字?jǐn)?shù): 1675字更新時間: 2021-03-26 23:06:31
2.3 數(shù)據(jù)一致性
半同步復(fù)制最主要的目標(biāo)是保證主從數(shù)據(jù)強一致性。要想搞清楚這其中的原理和具體實現(xiàn),需要研讀MySQL源代碼。MySQL semi-sync半同步以插件方式引入,源代碼在plugin/semisync目錄下。阿里有一篇很好的文章(http://mysql.taobao.org/monthly/2017/04/01/),從官方MySQL 5.7源代碼的層面分析了半同步復(fù)制的數(shù)據(jù)一致性問題。
在半同步方式中,主庫在等待從庫ACK時,如果超時則會退化為異步復(fù)制,這就可能導(dǎo)致數(shù)據(jù)丟失。主庫等待提交確認(rèn)的超時時間,由rpl_semi_sync_master_timeout參數(shù)控制,默認(rèn)值為10000毫秒。在下面的分析中,假設(shè)rpl_semi_sync_master_timeout足夠大,不會退化為異步方式。這里通過三個參數(shù)rpl_semi_sync_master_wait_point、sync_binlog、sync_relay_log的配置來對半同步復(fù)制進(jìn)行數(shù)據(jù)一致性的分析。
2.3.1 rpl_semi_sync_master_wait_point配置
(1)源代碼剖析

(2)設(shè)置為WAIT_AFTER_COMMIT
rpl_semi_sync_master_wait_point為WAIT_AFTER_COMMIT時,commitTrx的調(diào)用在引擎層提交之后(由ordered_commit函數(shù)中的process_after_commit_stage_queue調(diào)用),如圖2-6所示。即在等待從庫ACK時,雖然沒有返回當(dāng)前客戶端,但事務(wù)已經(jīng)提交,其他客戶端會讀取到已提交的事務(wù)。如果從庫還沒有讀到該事務(wù)在二進(jìn)制日志中的事件,同時主庫發(fā)生了崩潰,然后切換到從庫。那么之前讀到的事務(wù)就不見了,出現(xiàn)了幻讀,如圖2-7所示。

圖2-6 WAIT_AFTER_COMMIT處理流程

圖2-7 WAIT_AFTER_COMMIT導(dǎo)致幻讀
除了幻讀,這種場景還有一個問題是,如果客戶端會重新嘗試把該事務(wù)提交到新的主庫上,當(dāng)宕機的主庫重新啟動后,以從庫的身份重新加入到該主從結(jié)構(gòu)中,那么此時就會發(fā)現(xiàn),該事務(wù)在從庫中被提交了兩次,一次是之前作為主庫的時候,一次是被新主庫同步過來的,結(jié)果依然是主從數(shù)據(jù)不一致。
(3)設(shè)置為WAIT_AFTER_SYNC
MySQL針對上述問題,在5.7.2引入了Loss-less Semi-Synchronous。在調(diào)用binlog sync之后,引擎層提交之前等待從庫的ACK。這樣只有在確認(rèn)從庫收到事務(wù)的二進(jìn)制日志事件后,事務(wù)才會提交。在提交之前等待從庫ACK,同時可以堆積事務(wù),趨向group commit,有利于提升性能,如圖2-8所示。

圖2-8 WAIT_AFTER_SYNC處理流程
其實在圖2-8的流程中依然存在著導(dǎo)致主從數(shù)據(jù)不一致,使主從同步失敗的情形。進(jìn)一步的說明可見下面章節(jié)對sync_binlog配置的分析。
2.3.2 sync_binlog配置
(1)源代碼剖析

(2)設(shè)置分析
當(dāng)sync_binlog設(shè)置為0時,binlog sync磁盤由操作系統(tǒng)負(fù)責(zé)。當(dāng)不為0時,其數(shù)值為定期刷磁盤的binlog commit group次數(shù)。當(dāng)sync_binlog值大于1時,sync binlog操作可能并沒有使binlog落盤。如果沒有落盤,事務(wù)在提交前,主庫掉電,然后恢復(fù),那么這個時候該事務(wù)被回滾。但是,從庫上可能已經(jīng)收到了該事務(wù)的二進(jìn)制日志事件并且執(zhí)行了,這個時候就會出現(xiàn)從庫事務(wù)比主庫多的情況,主從同步失敗。因此,如果要保持主從一致,則需要設(shè)置sync_binlog為1。
WAIT_AFTER_SYNC和WAIT_AFTER_COMMIT兩圖中Send Events的位置(圖2-6和圖2-8),也可能導(dǎo)致主從數(shù)據(jù)不一致,出現(xiàn)同步失敗的情況。實際在rpl_semi_sync_master_wait_point分析的圖中是sync binlog大于1的情況。根據(jù)上面源代碼,流程如圖2-9所示。主庫依次執(zhí)行flush binlog、update binlog position、sync binlog。如果主庫在update binlog position后,且sync binlog前掉電,主庫再次啟動后原事務(wù)就會被回滾,但可能出現(xiàn)從庫已經(jīng)獲取到事件,這也會導(dǎo)致從庫數(shù)據(jù)比主庫多的情況,結(jié)果主從同步失敗。

圖2-9 sync_binlog大于1時的處理流程
由于上面的原因,sync_binlog設(shè)置為1時,MySQL會在sync后更新二進(jìn)制日志坐標(biāo)。流程如圖2-10所示。這時,對于每一個事務(wù)都需要sync binlog,同時sync binlog和網(wǎng)絡(luò)發(fā)送二進(jìn)制日志事件會是一個串行的過程,性能會下降。

圖2-10 sync_binlog等于1時的處理流程
2.3.3 sync_relay_log配置
(1)源代碼剖析

(2)設(shè)置分析
在從庫的I/O線程中g(shù)et_sync_period獲得的是sync_relay_log的值。與sync_binlog對sync控制一樣,當(dāng)sync_relay_log不是1時,semisync返回給主庫的position可能沒有刷新到磁盤。開啟GTID時,在保證前面兩個設(shè)置正確的情況下,sync_relay_log不是1的時候,僅發(fā)生主庫或從庫的一次崩潰并不會造成數(shù)據(jù)丟失或者主從同步失敗的情況。如果發(fā)生從庫沒有sync relay log,主庫事務(wù)提交,客戶端觀察到事務(wù)提交,然后從庫崩潰,這樣從庫端就會丟失掉已經(jīng)回復(fù)主庫ACK的事務(wù),如圖2-11中所示的GTID:xx:1-40。

圖2-11 sync_relay_log不為1時從庫崩潰
但當(dāng)從庫再次啟動時,會從主庫同步丟失事務(wù)的二進(jìn)制日志事件。如果沒有來得及這樣做主庫就崩潰了,此時用戶訪問從庫就會發(fā)現(xiàn)數(shù)據(jù)丟失,如圖2-12所示。

圖2-12 主庫在從庫同步前崩潰導(dǎo)致數(shù)據(jù)丟失
通過上面這個例子可知,MySQL半同步復(fù)制如果要保證任意時刻發(fā)生一臺機器宕機都不丟失數(shù)據(jù),則需要同時設(shè)置sync_relay_log為1。對relay log的sync操作是在queue_event中,每個事件都要sync,所以sync_relay_log設(shè)置為1時,事務(wù)響應(yīng)時間會受到影響,對于涉及數(shù)據(jù)比較多的事務(wù)延遲會增加很多。
這么多分析后我們不難發(fā)現(xiàn),當(dāng)前原生的MySQL主從復(fù)制要同時滿足數(shù)據(jù)一致性、高可用和高性能,依然是力有不逮。
- Java范例大全
- PHP程序設(shè)計(慕課版)
- Oracle 12c中文版數(shù)據(jù)庫管理、應(yīng)用與開發(fā)實踐教程 (清華電腦學(xué)堂)
- Raspberry Pi 2 Server Essentials
- Learning Continuous Integration with TeamCity
- C專家編程
- Programming Microsoft Dynamics? NAV 2015
- Arduino Wearable Projects
- Mastering jQuery Mobile
- Delphi開發(fā)典型模塊大全(修訂版)
- Python Programming for Arduino
- Visual Basic語言程序設(shè)計基礎(chǔ)(第3版)
- Flutter從0基礎(chǔ)到App上線
- AI輔助編程Python實戰(zhàn):基于GitHub Copilot和ChatGPT
- 第五空間戰(zhàn)略:大國間的網(wǎng)絡(luò)博弈