<bdo id="4g88a"><xmp id="4g88a">
  • <legend id="4g88a"><code id="4g88a"></code></legend>

    Loading

    MYSQL 是如何保證binlog 和redo log同時提交的?

    MYSQL 一個事務在提交的時候能夠保證binlog和redo log是同時提交的,并且能在宕機恢復后保持binlog 和redo log的一致性。

    先來看看什么是redo log 和binlog,以及為什么要保持它們的一致性。

    什么是redo log,binlog

    redo log是innodb引擎層產生的日志, MYSQL從磁盤讀取數據的單位是一頁,當修改頁中某條數據時,該行所在的數據頁就變成了臟頁,由于臟頁并不會立馬刷新到磁盤,所以redo log會記錄下數據頁進行了哪些變動,用于服務崩潰時的數據恢復。redo log是固定大小的,由多個文件組成一個環形的結構,

    image.png

    redo log由兩個指針,write pos 和checkpoint,都是順時針移動,write pos 記錄redo log當前寫入的位置, checkpoint往前移動,就代表就移動過的redo log記錄的臟頁刷新到磁盤上。所以,write poscheckpoint 之間的位置就代表redo log 還可以寫的空間大小,當write pos等于checkpoint時,MYSQL則必須等待臟頁刷新完畢后才能繼續進行修改操作。

    binlog 是mysql server服務層產生的日志。兩者的用途也不一樣。binlog 則主要用于數據庫的備份,主從同步。binlog記錄的是行變化,記錄格式也有3種,statement,row,mixed,這里就不細講了。

    在了解了redo log 和binlog的含義和各自的作用后,我們先來看看它們在一次sql更新中是如何運作的。

    sql 更新過程詳解

    來看下在一次事務過程中,它們的工作機制。假設我們在進行修改操作,那么可以用下面的流程圖來表示,

    image.png

    1,首先判斷要修改的數據是否在內存里,沒有的話就從磁盤讀取到內存。

    2,寫入redo log,注意這里寫入的redo log僅僅是prepare狀態,只有等到正式提交的時候才會變成commit狀態。并且寫入redo log也不是直接落盤,其實是寫入到了redo log buffer 中,落盤時機受到innodb_flush_log_at_trx_commit 參數控制。

    MYSQL會有一個后臺線程,定時刷新redo log buffer 中的數據到磁盤上。除此以外,當innodb_flush_log_at_trx_commit 值為1時 redo log則會在在prepare階段將redo log buffer 中的數據落入磁盤。

    注意??????,這里說的事務提交的時候redo log buffer中的數據刷到磁盤上,并不僅僅是執行的當前事務,比如A,B兩個事務,A事務執行到一半,寫了部分數據到redo log buffer,那么B此時提交事務,同樣也會將A事務的redo log 刷到磁盤上。

    innodb_flush_log_at_trx_commit 值為 0 時,redo log buffer則不會在prepare或者事務提交時刷盤,而是由后臺定時任務定時刷新redo log buffer中的數據到磁盤上。

    innodb_flush_log_at_trx_commit 值為2時,則是將redo log buffer中的內容刷新到文件系統緩存中,由操作系統決定何時刷新到磁盤上。

    所以可以看到,在事務執行過程中,redo log是可能一部分在內存,一部分已經落入磁盤了

    3, 在寫完redo log后,會去寫binlog,寫binlog同樣不是直接寫文件,而是寫到binlog cache中,那么binlog是何時刷新到磁盤上呢,這個是由sync_bin參數決定的。

    • sync_binlog = 0 :提交事務時,將內存中的binlog cache寫到文件系統緩存中,后續交由操作系統決定何時將數據持久化到磁盤

    • sync_binlog = 1 :提交事務時,將binlog cache中的數據寫入到文件系統緩存,并立馬刷新到磁盤。

    • sync_binlog =N(N>1) :提交事務時,都寫到文件系統緩存,但累積 N 個事務后才 fsync 刷新到磁盤。

    4, 最后一步便是對事物進行提交,按參數設置分別對redo log和binlog進行落盤處理。

    為什么要保證binlog 和redo log 同時提交

    看完了整個sql更新過程,先說下結論,innodb_flush_log_at_trx_commitsync_binlog都設置為1 能夠保證binlog 和redo log 同時提交。

    再來看看如果redo log和binlog不同時提交會導致什么問題?

    redo log和binlog不同時提交會導致主備不一致

    如果在一個事務提交過程中, binlog寫入成功了,此時主庫宕機,redo log寫入失敗,主庫恢復后,那么binlog可能就會被從庫拿去執行,然而主庫的redo log是沒有修改數據的,所以造成主備不一致。

    換過來,redo log寫入成功,但是binlog提交失敗,從庫就會缺失新的修改數據,造成主備不一致。

    兩階段提交避免數據不一致

    接著,我們來細聊 MYSQL在上述sql更新過程中,是如何保證redo log和binlog是同時提交的。

    上述事務執行過程中,可以看到對于redo log的提交分了兩個階段,第一個是redo log的prepare 階段,第二個是commit階段。

    宕機恢復時,redo log執行恢復的邏輯概括如下,

    1,只要redo log變成了commit狀態,MYSQL就認為事務是成功了。

    2,而恢復時,發現redo log是prepare 狀態的話,就會去判斷對應事務的binlog 是否完整,完整則對還未提交的事務進行提交,不完整則回滾事務。

    我們來分析下異常的情況:

    image.png

    如上圖所示,

    1,在第一種異常情況下,redo log 和binlog都沒有寫入,主備是一致的。

    2,第二和第三種異常情況, redo log已經落入磁盤,最后就看binlog是否完整了,完整宕機恢復后進行事務提交,備庫即使得到binlog,也能保證與主庫恢復后事務提交的數據 保持一致。

    ??????需要注意的是,innodb_flush_log_at_trx_commit 為1時才能保證redo log是在binlog寫入前是已經落盤的,如果是0或者2,則有可能出現節點崩潰時,redo log沒有寫入到磁盤而丟失,而binlog是完整的情況,造成主備不一致。

    兩階段提交帶給業務開發上的思考??

    從MYSQL 實現兩階段提交的邏輯,可以歸納下,它是如何做到對兩個業務做到最終一致的。

    我舉個業務上的例子, 比如有A,B兩個服務,A服務依賴B服務,如何保證在A服務上的數據操作和請求B服務接口這兩個動作同時成功或失敗?

    我直接說下結論,

    借鑒兩階段提交的邏輯,我們可以將A服務的數據操作在業務設計上增加一個預扣減的概念,先鎖定A服務數據資源,然后去請求B服務的接口,失敗的話,則釋放A服務鎖定的數據資源,成功的話則進行真實的扣減。

    除此以外,還需要增加一個對A服務數據進行補償修復的定時任務,類似與MYSQL數據庫宕機根據binlog是否完整看事務是否提交一樣,定時任務定期查看還沒有終結的A服務數據,拎出來請求B服務查看業務成功狀態,B服務返回成功,則將A服務的業務數據進行真實扣減,否則釋放A服務鎖定的數據資源。

    通過兩階段提交,來查看業務的最終一致性。

    最后,

    自薦一波?:

    歡迎朋友們關注我的公眾號????:【藍胖子的編程夢】!

    學習容器知識??,性能監控??,Golang?? 相關編程知識

    posted @ 2024-03-14 13:03  藍胖子的編程夢  閱讀(310)  評論(0編輯  收藏  舉報
    免费视频精品一区二区_日韩一区二区三区精品_aaa在线观看免费完整版_世界一级真人片
    <bdo id="4g88a"><xmp id="4g88a">
  • <legend id="4g88a"><code id="4g88a"></code></legend>