自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MySQL 事務(wù)兩階段提交原理簡析

數(shù)據(jù)庫 MySQL
MySQL 中的日志非常重要,包括實(shí)例內(nèi)的事務(wù)以及實(shí)例間的主從復(fù)制均基于日志實(shí)現(xiàn)。

引言

MySQL 中的日志非常重要,包括實(shí)例內(nèi)的事務(wù)以及實(shí)例間的主從復(fù)制均基于日志實(shí)現(xiàn)。

計劃通過多篇文章分析多種日志,從而串聯(lián)日志、事務(wù)、復(fù)制三個模塊之間的關(guān)系,本文是第一篇文章,介紹兩階段提交。

其中首先介紹為什么需要兩階段提交,然后簡單分析兩階段提交的實(shí)現(xiàn),期間介紹相關(guān)知識點(diǎn),包括分布式事務(wù)與崩潰恢復(fù)。

概念

兩份日志

MySQL 中最重要的兩份日志是 redo log 與 binlog。

為什么會有兩份日志,原因是使用場景不同。

其中:

  • redo log 用于實(shí)現(xiàn)事務(wù)的持久性,具體是通過 crash-safe 能力;
  • binlog 用于實(shí)現(xiàn)主從復(fù)制與數(shù)據(jù)恢復(fù)。

兩份日志主要有以下三點(diǎn)不同;

  • redo log 是 InnoDB 存儲引擎層實(shí)現(xiàn)的特有的日志,binlog 是 Server 層實(shí)現(xiàn)的通用的日志;
  • redo log 是物理日志,binlog 是邏輯日志;
  • redo log 是循環(huán)寫入,binlog 是追加寫入。

兩階段提交

為了保證兩份日志之間的邏輯一致,也就是數(shù)據(jù)與備份的一致性,引入兩階段提交(two-phase commit protocol,2PC)。

為什么需要兩階段提交,那么如果沒有兩階段提交,會發(fā)生什么呢?

由于 redo log 和 binlog 是兩個獨(dú)立的邏輯,如果不用兩階段提交,要么就是先寫完 redo log 再寫 binlog,或者采用反過來的順序。

假設(shè)執(zhí)行 update,將值從 1 改為 2。

假設(shè):

  • 先寫 redo log 后寫 binlog,如果 redo log 寫完后 MySQL 進(jìn)程異常重啟,redo log 崩潰恢復(fù)后值為 2,但是基于 binlog 備份恢復(fù)值為 1,并導(dǎo)致備份恢復(fù)少了一個事務(wù);
  • 先寫 binlog 后寫 redo log,如果 binlog 寫完后 MySQL 進(jìn)程異常重啟,基于 binlog 備份恢復(fù)值為 2,但是 redo log 還沒寫因此崩潰恢復(fù)后事務(wù)無效,值為 1,并導(dǎo)致備份恢復(fù)多了一個事務(wù)。

顯然,如果沒有兩階段提交,無法保證數(shù)據(jù)與日志的一致性。

那么,有兩階段提交時會怎么樣呢?

首先,介紹下兩階段提交的過程,其中將 redo log 的提交拆分為兩個步驟,包括 prepare 與 commit,期間寫入 binlog。

因此,如果在兩階段提交的不同時刻,MySQL 異常重啟會發(fā)生什么呢?

  • 如果在時刻 A 重啟,也就是 redo log prepare 之后,寫入 binlog 之前,崩潰恢復(fù)時發(fā)現(xiàn) redo log 沒有 commit,因此回滾。binlog 還沒寫,因此不會傳到備庫,數(shù)據(jù)與日志保持一致;
  • 如果在時刻 B 重啟,也就是寫入 binlog 之后,redo log commit 之前,崩潰恢復(fù)時發(fā)現(xiàn) redo log 雖然沒有 commit,但是 redo log 有完整的 prepare,且對應(yīng)的事務(wù) binlog 完整,因此提交事務(wù)。binlog 寫入,因此會傳到備庫,數(shù)據(jù)與日志保持一致。

崩潰恢復(fù)

從前一節(jié)的描述中可以發(fā)現(xiàn)崩潰恢復(fù)時根據(jù)兩階段提交的進(jìn)度進(jìn)行處理。

參考 MySQL 45 講,崩潰恢復(fù)(crash-recovery)時的完整判斷邏輯為:

  • 如果 redo log 里面的事務(wù)完整,也就是已經(jīng)有了 commit 標(biāo)識,直接提交;
  • 如果 redo log 里面的事務(wù)只有完整的 prepare,進(jìn)一步判斷對應(yīng)的事務(wù) binlog 是否存在且完整:
  • 如果是,提交事務(wù);
  • 否則,回滾事務(wù)。

因此,redo log prepare 后 commit 前崩潰恢復(fù)時可能發(fā)生回滾或提交,具體與 binlog 的完整性有關(guān)。

顯然,時刻 B 發(fā)生 crash 的情況對應(yīng) redo log prepare 完整,且 binlog 完整的場景,因此事務(wù)提交。

這里可以提出以下兩個問題:

1)如何判斷 binlog 完整

2)如何根據(jù) redo log 定位對應(yīng)的 binlog

接下來分別回答這兩個問題。

1)如何判斷 binlog 完整

判斷 binlog 的完整性有以下兩種方式:

  • 在事務(wù)提交時記錄 XID event 到 binlog 中以標(biāo)記事務(wù)的結(jié)束。這個機(jī)制確保了事務(wù)的完整性和一致性,無論使用哪種復(fù)制格式;
  • 在 MySQL 5.6.2 版本以后,還引入了 binlog-checksum 參數(shù),用于驗(yàn)證 binlog 內(nèi)容的正確性。通過為 binlog 中的每個事件添加校驗(yàn)和(checksum),MySQL 能夠檢測到寫入 binlog 時由于磁盤錯誤等原因?qū)е碌臄?shù)據(jù)損壞。

如下所示,測試顯示 row 與 statement 兩種 bnlog 格式中事務(wù)的最后一個 event 都是 XID event。

2)如何根據(jù) redo log 定位對應(yīng)的 binlog

redo log 與 binlog 有一個共同的數(shù)據(jù)字段,稱為 XID。

崩潰恢復(fù)的時候,會按順序掃描 redo log:

  • 如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
  • 如果碰到只有 parepare、而沒有 commit 的 redo log,就拿著 XID 去 binlog 找對應(yīng)的事務(wù)。

其中:

  • redo log 掃描的起點(diǎn)是 InnoDB 最后一次 checkpoint 操作的 lsn(last_checkpoint_lsn)。
  • XID 與分布式事務(wù)有關(guān),下一節(jié)中介紹。

這里可以提出另一個問題,根據(jù)事務(wù)的持久性,到什么進(jìn)度后事務(wù)將無法回滾?

理論上 MySQL 中通過 redo log 實(shí)現(xiàn)事務(wù)的持久性,因此 redo log 刷盤后就可以保證對數(shù)據(jù)庫的修改是永久性的,即使發(fā)生崩潰也不會丟失,當(dāng)然也不會回滾。

不過根據(jù)事務(wù)的兩階段提交協(xié)議,binlog 寫入代表事務(wù)提交,同樣不可能發(fā)生回滾。

因此,事務(wù)無法回滾的關(guān)鍵點(diǎn)是事務(wù)的提交,而不是單純的 redo log 或 binlog 的寫入。在事務(wù)提交的過程中,兩階段提交機(jī)制確保了 redo log 和 binlog 的一致性,這個提交過程標(biāo)志著事務(wù)從可回滾轉(zhuǎn)變?yōu)椴豢苫貪L。

XA 事務(wù)

分布式事務(wù)是一種跨多個獨(dú)立的數(shù)據(jù)庫、系統(tǒng)或網(wǎng)絡(luò)區(qū)域的事務(wù)處理方法。

XA 事務(wù)是一種遵循 XA 規(guī)范的分布式事務(wù),因此 XA 事務(wù)是分布式事務(wù)的一種實(shí)現(xiàn)。

XA 事務(wù)依賴兩階段提交(2PC)協(xié)議實(shí)現(xiàn)分布式事務(wù)的一致性和原子性。

兩階段提交是最常見的分布式事務(wù)協(xié)議,用于保證分布式事務(wù)的原子性,顯然并不是 MySQL 獨(dú)有的。

根據(jù) XA 規(guī)范,兩階段提交的實(shí)現(xiàn)過程中包括兩個角色:

  • 資源管理器(Resource Manager),可以稱為執(zhí)行器,用于管理分布式數(shù)據(jù)庫的一個本地事務(wù);
  • 事務(wù)管理器(Transaction Manager),可以稱為協(xié)調(diào)器,用于協(xié)調(diào)事務(wù)的提交、回滾、崩潰恢復(fù)。

兩階段提交中將提交操作分為兩個階段:

  • prepare 階段,協(xié)調(diào)器詢問所有執(zhí)行器,是否可以提交事務(wù),如果任何一個執(zhí)行器的本地事務(wù)無法提交時,分布式事務(wù)都需要通知所有執(zhí)行器進(jìn)行回滾操作;
  • commit 階段,協(xié)調(diào)器在收到每一個執(zhí)行器的提交確認(rèn)后,通知執(zhí)行器各自提交自己的本地事務(wù)。

MySQL 中的 XA 事務(wù)分為外部 XA 與內(nèi)部 XA。其中:

  • 外部 XA,MySQL 服務(wù)器作為執(zhí)行器,連接服務(wù)器的客戶端程序作為協(xié)調(diào)器,對應(yīng)多個支持分布式事務(wù)的數(shù)據(jù)庫實(shí)例,比如多套 MySQL(使用分庫分表中間件)、Oracle + MySQL;
  • 內(nèi)部 XA,對應(yīng)單個 MySQL 實(shí)例,分為以下兩種場景:
  • 沒有開啟 binlog,SQL 語句涉及一個或多個支持事務(wù)的存儲引擎;

開啟 binlog,SQL 語句涉及一個或多個支持事務(wù)的存儲引擎。

其中,由于 binlog 與存儲引擎是獨(dú)立單元,可以將 binlog 也看作一個存儲引擎,因此需要通過 XA 事務(wù)實(shí)現(xiàn) binlog 與存儲引擎的數(shù)據(jù)一致性和原子性,從而保證全部操作要么全部提交,要么全部回滾。

在分布式事務(wù)中,XID作為全局事務(wù)的唯一標(biāo)識符,用于跟蹤和協(xié)調(diào)不同數(shù)據(jù)庫實(shí)例中的事務(wù)部分。這個標(biāo)識符在事務(wù)的所有參與者之間是共享的,以確保事務(wù)的一致性和完整性。

因此在 XA 事務(wù)中,XID用于在多個數(shù)據(jù)庫實(shí)例之間協(xié)調(diào)事務(wù)。

在 MySQL 中,XID(Transaction Identifier)是事務(wù)的唯一標(biāo)識符,用于標(biāo)記事務(wù)的提交。

binlog 中一個事務(wù)由一系列事件(event)組成,這個序列由 BEGIN 事件開始,以 XID 事件結(jié)束(對于提交的事務(wù))。

因此如果事務(wù)被回滾,不會記錄 XID 事件,而是記錄一個 ROLLBACK 事件。

參考 chatgpt,XID 與 GTID 的主要區(qū)別包括:

  • XID:是事務(wù)的標(biāo)識符,用于標(biāo)記事務(wù)的結(jié)束,主要用于事務(wù)的恢復(fù)和復(fù)制過程中確定事務(wù)邊界。對于分布式事務(wù),所有 MySQL 實(shí)例使用相同的 XID 來提交事務(wù);
  • GTID(全局事務(wù)標(biāo)識符):是 MySQL 5.6 及更高版本中引入的,用于唯一標(biāo)識每個事務(wù)。每個 GTID 都是全局唯一的,即使在不同的 MySQL 實(shí)例中也是如此。GTID 使得跟蹤和復(fù)制事務(wù)變得更加簡單和可靠。

實(shí)現(xiàn)

prepare

參考文章 MySQL 事務(wù)二階段提交 與 MySQL 核心模塊揭秘 | 07 期 | 二階段提交 (1) prepare 階段,prepare 階段做的事情分為兩類:

  • binlog prepare,對應(yīng) binlog_prepare 函數(shù),什么都不做;
  • InnoDB prepare,對應(yīng) innobase_xa_prepare 函數(shù),具體做五件事情:
  • 把分配給事務(wù)的所有 Undo segment 的狀態(tài) TRX_UNDO_STATE 從 TRX_UNDO_ACTIVE 修改為 TRX_UNDO_PREPARED;
  • 把事務(wù) XID 寫入所有 Undo segment 中當(dāng)前提交事務(wù)的 Undo Log Segment Header;
  • 把內(nèi)存中的事務(wù)對象狀態(tài)從 TRX_STATE_ACTIVE 修改為 TRX_STATE_PREPARED,標(biāo)識事務(wù)已經(jīng)進(jìn)入二階段提交的 prepare 階段;
  • 如果當(dāng)前提交事務(wù)的隔離級別是讀未提交(READ-UNCOMMITTED)或讀已提交(READ-COMMITTED),InnoDB 會釋放事務(wù)給記錄加的共享、排他 GAP 鎖;
  • 調(diào)用 trx_flush_logs(),處理 redo log 刷盤的相關(guān)邏輯,其中實(shí)際上并不會將 redo log 刷盤,也就是同樣什么都不做。

其中 undo log 非常重要,原因是:

  • TRX_UNDO_STATE 用于崩潰恢復(fù)過程中,標(biāo)記哪些事務(wù)需要恢復(fù),哪些事務(wù)不用恢復(fù)。
  • XID 用于崩潰恢復(fù)過程中,決定數(shù)據(jù)庫崩潰時處于 prepared 階段的事務(wù),是要回滾還是要提交。

參考文章 XA事務(wù)與兩階段提交。

Undo頁面鏈表的第一個頁面的結(jié)構(gòu)見下圖,其中記錄了一些關(guān)于這個事務(wù)的一些屬性。

其中 Undo Log Segment Header 結(jié)構(gòu)見下圖,其中 TRX_UNDO_STATE 字段表示事務(wù)所處的狀態(tài)。

其中 Undo Log Header 結(jié)構(gòu)見下圖。

其中:

  • TRX_UNDO_XID_EXISTS:表示有沒有 XID 信息;
  • XID信息:表示具體的 XID 是什么。

TRX_UNDO_STATE 的取值包括:

  • TRX_UNDO_ACTIVE:活躍狀態(tài),也就是一個活躍的事務(wù)正在往這個段里邊寫入 undo log;
  • TRX_UNDO_CACHED:被緩存的狀態(tài)。處在該狀態(tài)的 Undo 頁面鏈表等待著之后被其他事務(wù)重用;
  • TRX_UNDO_TO_FREE:對于 insert undo 鏈表來說,如果在它對應(yīng)的事務(wù)提交之后,該鏈表不能被重用,那么就會處于這種狀態(tài)。Undo 頁面鏈表可以被馬上清理;
  • TRX_UNDO_TO_PURGE:對于 update undo 鏈表來說,如果在它對應(yīng)的事務(wù)提交之后,該鏈表不能被重用,那么就會處于這種狀態(tài)。Undo 頁面鏈表不可以被馬上清理,而是加入 History 鏈表用于 MVCC,等待 purge 線程清理;
  • TRX_UNDO_PREPARED:包含處于 prepare 階段(這個階段是在分布式事務(wù)中會出現(xiàn))的事務(wù)產(chǎn)生的 undo log。

commit

commit 階段做的事情同樣分為兩類:

  • binlog 刷盤,對應(yīng) flush 函數(shù),將事務(wù)執(zhí)行過程中產(chǎn)生的 binlog 寫入硬盤;
  • InnoDB commit,對應(yīng) innobase_commit 函數(shù),完成存儲引擎層面的事務(wù)提交。

具體 commit 階段的實(shí)現(xiàn)與組提交有關(guān),計劃下一篇文章中介紹。

因此,在客戶端執(zhí)行 commit 語句或自動 commit 時,MySQL 開啟內(nèi)部 XA 事務(wù),分兩階段完成 XA 事務(wù)的提交。

崩潰恢復(fù)

崩潰恢復(fù)全過程分為多個階段,其中與事務(wù)兩階段提交有關(guān)的階段包括:

  • 恢復(fù)數(shù)據(jù)頁,通過 doublewrite buffer 修復(fù)部分頁寫入(partial page write)導(dǎo)致的數(shù)據(jù)頁損壞;
  • 讀取 redo log,從 last_checkpoint_lsn 開始讀取 redo log;
  • 應(yīng)用 redo log 到數(shù)據(jù)頁,將沒有寫入數(shù)據(jù)頁的日志重做一遍,從而保證事務(wù)的持久性;
  • 初始化事務(wù)子系統(tǒng),從 undo 表空間文件讀取未完成的事務(wù);
  • 處理未完成事務(wù),其中:
  • 如果事務(wù) XID 對應(yīng) binlog 已寫入文件,事務(wù)提交;
  • 如果事務(wù) XID 對應(yīng) binlog 未寫入文件,事務(wù)回滾。
  • 清理已提交事務(wù),對應(yīng) TRX_STATE_COMMITTED_IN_MEMORY,包括 DDL 與 DML;
  • 回滾未提交事務(wù),對應(yīng) TRX_STATE_ACTIVE,包括 DDL 與 DML;
  • 處理 prepare 事務(wù),對應(yīng) TRX_STATE_PREPARED,其中:

未完成事務(wù)的狀態(tài)可能是以下三種之一:

  • TRX_STATE_ACTIVE,表示事務(wù)還沒有進(jìn)入提交階段。
  • TRX_STATE_PREPARED,表示事務(wù)已經(jīng)提交了,但是只完成了二階段提交的 PREPARE 階段,還沒有完成 COMMIT 階段。
  • TRX_STATE_COMMITTED_IN_MEMORY,表示事務(wù)已經(jīng)完成了二階段提交的 2 個階段,還剩一些收尾工作沒做,這種狀態(tài)的事務(wù)修改的數(shù)據(jù)已經(jīng)可以被其它事務(wù)看見了。

其中未提交事務(wù) TRX_STATE_ACTIVE 對應(yīng) redo log 已經(jīng)刷盤的未提交事務(wù),包括以下三種場景:

  • 后臺線程定時將 redo log buffer 中的日志刷盤時將事務(wù)執(zhí)行中間過程的 redo log 持久化到磁盤;
  • redo log buffer 占用的空間即將達(dá)到 innodb_log_buffer_size 一半時,后臺線程會主動寫盤,即使事務(wù)并沒有提交;
  • 并行的事務(wù)提交時,順帶將這個事務(wù)的 redo log buffer 持久化到磁盤。假設(shè)一個事務(wù) A 執(zhí)行到一半,已經(jīng)寫了一些 redo log 到 buffer 中,這時候有另外一個線程的事務(wù) B 提交,如果 innodb_flush_log_at_trx_commit 設(shè)置的是 1,那么按照這個參數(shù)的邏輯,事務(wù) B 要把 redo log buffer 里的日志全部持久化到磁盤。這時候,就會帶上事務(wù) A 在 redo log buffer 里的日志一起持久化到磁盤。

因此,為了保證事務(wù)的原子性,需要在崩潰恢復(fù)時將這些未提交事務(wù)回滾,而找到這些未提交事務(wù)依賴 undo log。

結(jié)論

MySQL 通過事務(wù)的兩階段提交實(shí)現(xiàn)數(shù)據(jù)與日志的一致性。

其中數(shù)據(jù)指 redo log,日志指 binlog,可以認(rèn)為是兩個不同的存儲引擎,因此基于分布式事務(wù)的 XID 協(xié)議實(shí)現(xiàn)一致性。

具體實(shí)現(xiàn)中將 redo log 的提交拆分為兩個步驟,包括 prepare 與 commit,期間寫入 binlog。

因此,寫入的不同階段異常重啟時:

  • redo log commit crash,binlog 完整,因此事務(wù)提交;
  • binlog crash,redo log 沒有 commit,且沒有寫入 binlog,因此事務(wù)回滾。

具體是在崩潰恢復(fù)過程中基于兩階段提交保證事務(wù)的一致性。

其中:

  • redo log application 階段用于將沒有寫入數(shù)據(jù)頁的日志重做一遍,把系統(tǒng)恢復(fù)到崩潰前的狀態(tài),其中都是提交,沒有回滾;
  • 初始化事務(wù)子系統(tǒng)階段從表空間中找到各個 Undo 頁面鏈表的首個頁面的頁號,然后根據(jù)事務(wù)的狀態(tài)處理未完成事務(wù)。其中:
  • TRX_STATE_ACTIVE,表明是未提交事務(wù),因此回滾事務(wù);
  • TRX_STATE_PREPARED,進(jìn)一步判斷 XID 對應(yīng) binlog 是否存在,如果有,提交事務(wù),否則回滾事務(wù);
  • TRX_STATE_COMMITTED_IN_MEMORY,表明是已提交事務(wù),因此提交事務(wù),具體是清理已提交事務(wù)。

因此,可以將崩潰恢復(fù)過程中使用的日志的順序理解為 redo log、undo log、binlog。

責(zé)任編輯:華軒 來源: 丹柿小院
相關(guān)推薦

2022-12-21 19:04:35

InnoDBMySQL

2022-03-28 10:44:51

MySQL日志存儲

2023-07-26 09:24:03

分布式事務(wù)分布式系統(tǒng)

2024-01-26 08:18:03

2024-12-06 07:10:00

2017-08-30 18:15:54

MySql

2018-10-29 08:44:29

分布式兩階段提交事務(wù)

2023-11-29 07:47:58

DDIA兩階段提交

2023-12-05 09:33:08

分布式事務(wù)

2023-01-18 10:35:49

MySQL數(shù)據(jù)庫

2025-04-07 03:00:00

MySQLDDLonline

2022-07-27 08:52:10

MySQL二階段提交

2020-02-03 12:12:28

MySQL數(shù)據(jù)庫SQL

2024-07-22 08:57:58

2010-04-20 20:46:01

負(fù)載均衡

2024-03-26 16:24:46

分布式事務(wù)2PC3PC

2015-05-13 10:36:43

2021-10-12 19:12:15

單步實(shí)現(xiàn)系統(tǒng)

2023-10-24 08:25:20

TCC模式事務(wù)

2011-07-12 14:04:58

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號