MySQL主從資料不一致,怎麼辦?

張哥說技術發表於2022-12-20

先給大家說個身邊的故事。


小夥伴二狗最近面宇宙廠,前面被問MySQL索引、鎖、主從複製原理時答的都很開心。

面試官問到 :“你們遇到主從不一致的問題怎麼解決呢?你有什麼更好的方案嗎?

二狗懵了。不就是讀寫時候走主,純讀走從嗎。難道還有什麼別的辦法?

面試官:emmm……有。那我們換個問題,主從複製的方式有幾種,你能講講嗎?

二狗:…… 問到盲點了

面試官:答不上來沒關係,您的情況我基本瞭解了。感謝您參加本次面試,後續結果HR會聯絡你。


如果你也有類似上述疑惑,那麼這篇文章拿好了!

點選上方“後端開發技術”,選擇“設為星標” ,質資源及時送達

主從資料不一致問題

先介紹下問題產生的背景。

在MySQL 一主多從的架構中,主要有兩種,一種是透過客戶端直連,一種是透過代理 prxoy 間接連線。

客戶端直連模式下一般會把資料庫的連線資訊放在客戶端的連線層,客戶端做負載均衡,由客戶端來選擇後端資料庫進行查詢。透過代理的模式下 MySQL 和客戶端之間有一箇中間代理層 proxy,客戶端直連線 proxy, 由 proxy 根據請求型別和上下文決定請求的分發路由。

MySQL主從資料不一致,怎麼辦?

客戶端直連和帶 proxy 的讀寫分離架構,各有哪些特點。

  1. 客戶端直連方案,整體架構簡單,排查問題方便。但是這種方案,由於要了解後端部署細節,所以在出現主備切換、庫遷移等操作的時候,處理起來比較麻煩,對開發人員要求高。
  2. 直連 proxy 的架構,對客戶端比較友好。客戶端不需要關注後端細節,但是 proxy 也需要有高可用架構,架構更復雜,對運維團隊要求更高。
MySQL主從資料不一致,怎麼辦?

目前趨勢是直連 proxy 的方向發展,具體取捨取決於自身情況。

帶來的問題

無論哪種架構,都會遇到主從資料不一致的問題:由於主從同步可能存在延遲,客戶端執行完一個更新事務後馬上發起查詢,如果查詢選擇的是從庫的話,就有可能讀到剛剛的事務更新之前的狀態。如何解決呢?

在進行讀寫分離的同時,解決主從同步中資料不一致的問題,主要有幾種思路,一是從業務層面解決,二是從檢測主從同步的差異上解決,三也是最根本的方式——從主從之間資料複製方式解決。按照這三種指導思想,我總結了一下有以下幾種方案。

全部走主庫方案

先說一種最為廣泛使用的方案,也就是二狗的回答。

如果是在同一資料庫中對資料進行更新的時候,可以對記錄加寫鎖,這樣在讀取的時候就不會發生資料不一致的情況。所以我們可以根據業務場景來做區分。如果是需要寫完之後必須讀區到準確資料的場景,可以直接全部走主庫,比如訂單支付、金融業務等強一致性場景;如果業務允許讀到舊資料,可以在讀的時候查詢從庫。

這樣的話你可能覺得並沒有真正解決主從不一致的問題,但其實在實際生產中,大多數情況下都這麼解決的。優點是簡單成本低,不需要複雜架構。缺點也很明顯,如果業務一致性要求很高,很多讀都會在主庫進行,並沒有真正的讀寫分離減輕主庫壓力,讀寫分離成了擺設。MySQL主從資料不一致,怎麼辦?

延遲查詢方案

比如主庫更新一條資料以後,查詢從庫資料的時候後端介面可以線sleep一段時間,比如絕大多數主從同步都可以在1s內完成,那就可以sleep一秒鐘的時間。或者也可以延遲查詢邏輯放在前端,比如訂單支付完成後,可以前端延遲一秒再發起Ajax請求。

缺點是並不能資料保證完全準確,如果資料同步延遲大於一秒則主從依舊不一致;另一個是造成介面響應時間變長,甚至造成服務的吞吐量降低,對於不需要等待1s的場景,也必須等待夠1s。

判斷主備無延遲方案

在構建MySQL主從架構的文章裡提過一些關於主從搭建以及狀態檢視的具體操作,具體請看下面這篇文章?

MySQL主從資料不一致,怎麼辦?

快速入門Mycat及主從搭建指南


後文將要提到的關於判斷主備延遲的關鍵引數便是從 status 的結果中獲得的。

1.判斷 Seconds_Behind_Master

透過 show slave status 可以拿到 Seconds_Behind_Master 的值,表示主備延遲的時間長短。判斷  Seconds_Behind_Master 是否為 0,如果不等於0,就等到這個引數為0時再執行查詢。但是這個延遲時間的單位為秒,所以可能產生一秒內的誤差。

MySQL主從資料不一致,怎麼辦?

2.判斷同步binlog 位置

在同步時透過檔名+檔案位置就可以定位到binlog檔案正在同步的位置。Master_Log_FileRead_Master_Log_Pos,表示的是讀到的主庫的最新位點;Relay_Master_Log_FileExec_Master_Log_Pos,表示的是備庫執行的最新位點。

MySQL主從資料不一致,怎麼辦?

3.判斷GTID相同

GITD在全域性唯一,並且可以動過GTID來定位binlog位置,所以也可以用來判斷主備是否有延遲

什麼是GTID?

GTID特性是5.6加入的一個強大的特性,全稱是Global Transaction Identifier。MySQL會為每一個DML/DDL操作增加一個唯一標記叫做GTID,這個標記在整個複製環境中都是唯一的。主從環境中主庫的DUMP執行緒可以直接透過GTID定位到需要傳送的binary log位置,而不再需要指定binary log的檔名和位置,因此切換極為方便。關於DUMP執行緒是如何透過GTID定位到binary log位置的,我們將在第17節進行討論。

Auto_Position=1 ,表示這對主備關係使用了 GTID 協議。Retrieved_Gtid_Set,是備庫收到的所有日誌的 GTID 集合;Executed_Gtid_Set,是備庫所有已經執行完成的 GTID 集合。對比這兩個集合對應的GTID相同就表示主備之間無延遲。

但是需要說明的是,這裡的無延遲,指的其實是從庫收到主庫的資料已經全部執行結束。但可能有種情況,客戶端已經收到資料,而對應的binlog並沒有收到,這種情況下從庫是不知道的。

如何解決這個問題呢?

我們現在的困境是不知道主庫到底有沒有新執行的資料,從庫有沒有把最新binlog收到並且執行,那如果主庫執行完SQL我就拿到最新binlog位點呢?還有另一種思路,之所以產生這種情況,是因為5.7版本mysql預設採用了非同步複製的方式。如果想要資料更精確,就有必要在複製方式上做出改變。

對於這兩種思路又產生了後面幾種解決方案。

從庫等指定位點或GTID方案

對於前面提到的思路,剛好MySQL有這樣一個命令。

select master_pos_wait(file, pos[, timeout]);

這條命令是在從庫執行的,引數 file 和 pos 指的是主庫上的檔名和位置,timeout 可選,設定為正整數 N 表示這個函式最多等待 N 秒。這個命令正常返回的結果是一個正整數 M,表示從命令開始執行,到應用完 file 和 pos 表示的 binlog 位置,執行了多少事務。

這樣就產生了一個方案。

  • 主庫事務更新完成後,馬上執行 show master status 得到當前主庫執行到的 File 和 Position。
  • 選定一個從庫執行查詢語句。
  • 在從庫上執行 select master_pos_wait(File, Position, 1)。
  • 如果返回值是 >=0 的正整數,則在這個從庫執行查詢語句,否則,到主庫執行查詢語句。

同樣的對於判斷位點,可有一套判斷GTID的方案。

select wait_for_executed_gtid_set(gtid_set, 1);

邏輯與前者相同,只需要等待GTID到指定數字即可,這裡大家舉一反三,不做贅述。

複製方式

下面就開始介紹關於複製方式方面的思路,從這些複製方式中,你會找到關於解決主備延遲問題的更優思路。先介紹一下非同步複製模式。

非同步複製

非同步模式就是客戶端提交 COMMIT 之後不需要等從庫返回任何結果,而是直接將結果返回給客戶端,這樣做的好處是不會影響主庫寫的效率,但可能會存在主庫當機,而 Binlog 還沒有同步到從庫的情況,也就是此時的主庫和從庫資料不一致。這時候從從庫中選擇一個作為新主,那麼新主則可能缺少原來主伺服器中已提交的事務。所以,這種複製模式下的資料一致性是最弱的。

預設情況下,MySQL就是非同步複製的模式。

MySQL主從資料不一致,怎麼辦?

半同步複製(增強半同步複製)

在MySQL5.5中加入了半同步複製(semisynchronous replication),主庫上的事務在儲存引擎層提交之後,需要等待從庫返回ACK訊號。並且在接收到從庫返回ACK訊號或者等待超時才會返回給客戶端一個提交結果。但是這樣會造成其他會話可以讀取到這些記錄,因為此時事務已經提交,這就造成了幻讀。

在MySQL5.7中進一步對半同步複製做了增強,將等待從庫返回ACK訊號的時間點提前了,新特性中主庫上的事務會在儲存引擎層提交之前一直等待從庫返回ACK訊號。這就意味著,在主庫crash的情況下,所有在主庫上已經提交的事務已經被複制到至少一個從庫上,這就解決了幻讀的問題,資料的一致性獲得了極大提升。

但是缺點也很明顯,會造成同步過程多了一次網路連線,降低主庫的寫吞吐量。在 MySQL5.7 版本中還增加了rpl_semi_sync_master_wait_for_slave_count引數 ,可以對從庫的應答數量進行設定,預設為 1,也就是說只要有 1 個從庫進行了響應,就可以返回給客戶端。在1主1從情況下,這接近同步複製,果使用semi-sync+位點判斷方案,這樣我們前面提到的主從資料不一致問題引刃而解,但是如果多從還是有可能出現不一致的情況。

MySQL主從資料不一致,怎麼辦?

MGR 組複製

MySQL在5.7.17 版本中引入了組複製技術,簡稱 MGR(MySQL Group Replication),這種複製技術是基於 分散式一致性協議Paxos 協議的,實現了分散式下資料的最終一致性。

A transaction received by Source 1 is executed. Source 1 then sends a message to the replication group, consisting of itself, Source 2, and Source 3. When all three members have reached consensus, they certify the transaction. Source 1 then writes the transaction to its binary log, commits it, and sends a response to the client application. Sources 2 and 3 write the transaction to their relay logs, then apply it, write it to the binary log, and commit it.

MySQL主從資料不一致,怎麼辦?

MGR 由若干個節點共同組成一個複製組,一個寫事務的提交,必須經過組內大多數節點(N / 2 + 1)決議並透過,才能得以提交。如上圖所示,由3個節點組成一個複製組,Consensus層為一致性協議層,在事務提交過程中,發生組間通訊,由2個節點決議(certify)透過這個事務,事務才能夠最終得以提交併響應,其實就是過半投票。

而針對只讀(RO)事務則不需要經過組內同意,直接 COMMIT 即可。在一個複製組內有多個節點組成,它們各自維護了自己的資料副本,並且在一致性協議層實現了原子訊息和全域性有序訊息,從而保證組內資料的一致性。雖然半同步複製部分解決了一致性問題,但只能在簡單架構下比如一主一從,MGR才真正解決了這個問題,並且MGR可以在多主的複雜情況下有效保證資料的一致性。

總結

總結一下,解決主從複製延遲一共可以有如下7種思路:

  1. 讀寫走主庫方案
  2. 延遲查詢方案
  3. 判斷主備無延遲方案
  4. 判斷同步位點方案
  5. 等待同步位點方案
  6. 半同步複製方案+等待位點
  7. 組複製MGR方案
其實在實際生產中,這些方案是可以混合使用的,因為一致性越強就意味著效能越低。比如業務不在乎是否查詢到過期資料就可以直接查詢從庫,如果需要的一致性強可以選擇後幾種方案。
最後,歡迎大家提問和交流。

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

相關文章