偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

MySQL 事務兩階段提交原理簡析

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

引言

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

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

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

概念

兩份日志

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

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

其中:

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

兩份日志主要有以下三點不同;

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

兩階段提交

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

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

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

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

假設:

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

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

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

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

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

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

崩潰恢復

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

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

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

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

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

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

1)如何判斷 binlog 完整

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

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

1)如何判斷 binlog 完整

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

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

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

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

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

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

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

其中:

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

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

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

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

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

XA 事務

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

實現(xiàn)

prepare

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

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

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

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

參考文章 XA事務與兩階段提交。

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

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

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

其中:

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

TRX_UNDO_STATE 的取值包括:

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

commit

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

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

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

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

崩潰恢復

崩潰恢復全過程分為多個階段,其中與事務兩階段提交有關的階段包括:

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

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

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

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

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

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

結(jié)論

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

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

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

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

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

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

其中:

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

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

責任編輯:華軒 來源: 丹柿小院
相關推薦

2022-12-21 19:04:35

InnoDBMySQL

2022-03-28 10:44:51

MySQL日志存儲

2023-07-26 09:24:03

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

2025-06-19 08:03:03

2025-06-10 08:02:15

2025-05-16 07:46:11

分布式事務服務

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

分布式兩階段提交事務

2023-11-29 07:47:58

DDIA兩階段提交

2023-12-05 09:33:08

分布式事務

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

負載均衡

2024-03-26 16:24:46

分布式事務2PC3PC

2015-05-13 10:36:43

點贊
收藏

51CTO技術棧公眾號