什麼?MySQL在從庫讀到了比主庫更加新的資料?

資料庫工作筆記發表於2023-12-08

來源:MySQL資料庫聯盟

上週,一個星球的夥伴問了一個問題:

一主兩從,一個從庫非同步, 一個從庫半同步的情況,主等半同步複製的ack的期間,那麼非同步複製的從庫,有沒有可能讀到比主庫更加新的資料?

這個問題,上週放到了我們的知識星球,大家也展開了討論。

所以這篇文章也是算大家一起創作的,感謝:

@InnerCodeDBA

@李努力

@無與倫比

@伴夏

@sky

@仲景

@大番薯


這裡就來總結一下這個問題。

當然,要說清這個問題,得先從兩階段提交說起:


兩階段是什麼?

第一階段:

寫Redo Log,進入Prepare階段。


第二階段:

寫Binlog

Redo Log 的Commit


兩階段提交的作用

其實兩階段提交的目的也是為了崩潰恢復:
比如MySQL在寫完Redo Log之後,寫Binlog之前,崩潰了
因為Binlog還沒寫,Redo log也沒提交,那MySQL啟動之後,事務回滾就行
如果MySQL在寫Binlog到Redo Log的Commit之間,崩潰了
就需要做一些判斷
1 如果Redo log是完整的,則事務直接提交
2 如果Redo log裡的事務只是Prepare狀態,就需要判斷Binlog是否完整:
Binlog完整,就提交事務,Binlog不完整,就回滾事務。


再來看幾種複製型別:

MySQL非同步複製

什麼?MySQL在從庫讀到了比主庫更加新的資料?

非同步複製的大致過程如下:

  1. SQL解析:解析客戶端發來的SQL;

  2. 儲存引擎處理:儲存引擎執行 SQL 操作;

  3. 寫Redo Log(Prepare)階段:事務的更改被準備好(prepare),但還沒有提交到資料庫;

  4. 寫binlog:記錄操作的Binlog;

  5. 傳到從的中繼日誌:把 binlog 裡面的內容傳給從庫的中繼日誌(relay log)中;

  6. Redo Log提交:事務在InnoDB儲存引擎中被提交。這時,更改正式應用到資料庫檔案中;

  7. 主庫確認提交:主庫在不等待從庫確認的情況下向客戶端返回commit成功;

  8. 從庫應用更改:從庫的 SQL 執行緒負責讀取它的 relay log 裡的資訊並應用到從庫資料庫中。

在非同步複製下,假如配置了自動切換的前提下,主庫突然當機,然後從提升為主時,原來主庫上可能有一部分已經完成提交的資料還沒來得及傳送到從庫,就可能產生資料丟失。為了解決這個問題,在 MySQL 5.5 版本中引入了半同步複製。下面來看下半同步複製的原理。


傳統半同步複製after_commit

什麼?MySQL在從庫讀到了比主庫更加新的資料?

半同步複製的大致過程如下:

  1. SQL解析

  2. 儲存引擎處理:執行SQL操作

  3. 寫Redo Log(Prepare)階段:事務的更改被準備好(prepare),但還沒有提交到資料庫;

  4. 寫binlog:記錄操作的Binlog;

  5. 把Binlog裡的內容傳給從庫的中繼日誌;

  6. Redo Log提交:事務在InnoDB儲存引擎中被提交。這時,更改正式應用到資料庫檔案中;

  7. 從庫傳送 ACK:從庫收到 binlog 後,傳送給主庫一個 ACK,表示收到了

  8. 主庫確認提交:主庫收到這個 ACK 以後,才向客戶端返回 commit 成功;

跟傳統非同步複製相比,半同步複製保證了所有客戶端傳送過確認提交的事務,從庫都已經收到這個日誌了。

但是這種模式下,實際上主庫已經將該事務 commit 到事務引擎層,只是在等待返回而已,而此時其他 session 已經可以看到資料發生了變化,如果此時主庫當機,有可能從庫還沒寫 Relay log,就會發生其他 session 切換前後查詢的資料不一致的情況。


增強半同步複製after_sync

什麼?MySQL在從庫讀到了比主庫更加新的資料?

增強半同步複製after_sync的大致流程:

  1. SQL解析

  2. 儲存引擎處理:執行SQL操作,但不提交事務

  3. 寫Redo Log(Prepare)階段:事務的更改被準備好(prepare),但還沒有提交到資料庫;

  4. 寫binlog

  5. Binlog裡的內容傳給從庫的中繼日誌

  6. 等待從庫確認:等待從庫成功接收binlog的返回ACK訊號

  7. Redo Log提交:在收到從庫的 ACK 訊號後,事務在InnoDB儲存引擎中被提交。這時,更改正式應用到資料庫檔案中;

  8. 反饋至客戶端:確認提交後向客戶端返回成功資訊


問題解析

回到上面的問題

一主兩從,一個從庫非同步, 一個從庫半同步的情況,主等半同步複製的ack的期間,那麼非同步複製的從庫,有沒有可能讀到比主庫更加新的資料?

我們也討論了很久,這裡就只貼出@sky 大佬給到的最終答案:

假如半同步複製是after_commit

主庫InnoDB層已經提交,其他資料庫連線已經可以看見等待ack返回的事務結果了,即使非同步從庫也可以看見,也不算是看到比主庫新的資料。

假如半同步複製是after_sync

Binlog已經落盤並且可以傳輸並回放到非同步從庫,但由於主庫InnoDB層未能提交事務,主庫其他資料庫連線無法檢視。此時非同步從庫有可能會檢視到比主庫更新的資料。

@InnerCodeDBA 針對這個問題,也畫了一張圖,方便我們理解:

什麼?MySQL在從庫讀到了比主庫更加新的資料?


為什麼要討論這個問題呢?後面又問了提問者,他說是業務上的一個真實場景:

有一套MySQL,主(A)從(B)之間是半同步複製,另外還有一個DTS監聽主庫的Binlog,同步到C。

但是開發遇到這樣一個問題,先查C,再查主A,發現讀到了舊資料,很多人會覺得:

既然DTS監聽到Binlog,那可能事務已經提交了,為什麼還會讀到舊資料?(其實這是一種誤解)


這種場景跟上面的分析也基本一致,因為DTS採用的是非同步複製。

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

相關文章