MySQL 核心模塊揭祕 | 12 期 | 創建 savepoint

回滾操作,除了回滾整個事務,還可以部分回滾。部分回滾,需要保存點(savepoint)的協助。本文我們先看看保存點裏面都有什麼。

作者:操盛春,愛可生技術專家,公衆號『一樹一溪』作者,專注於研究 MySQL 和 OceanBase 源碼。

愛可生開源社區出品,原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源

本文基於 MySQL 8.0.32 源碼,存儲引擎爲 InnoDB。

1. undo 日誌序號

InnoDB 的事務對象有一個名爲 undo_no 的屬性。事務每次改變(插入、更新、刪除)某個表的一條記錄,都會產生一條 undo 日誌。這條 undo 日誌中會存儲它自己的序號。這個序號就來源於事務對象的 undo_no 屬性。

也就是說,事務對象的 undo_no 屬性中保存着事務改變(插入、更新、刪除)某個表中下一條記錄產生的 undo 日誌的序號。

每個事務都維護着各自獨立的 undo 日誌序號,和其它事務無關。

每個事務的 undo 日誌序號都從 0 開始。事務產生的第 1 條 undo 日誌的序號爲 0,第 2 條 undo 日誌的序號爲 1,依此類推。

InnoDB 的 savepoint 結構中會保存創建 savepoint 時事務對象的 undo_no 屬性值。

2. savepoint 結構

我們通過 SQL 語句創建一個 savepoint 時,server 層、binlog、InnoDB 會各自創建用於保存 savepoint 信息的結構。

server 層的 savepoint 結構是一個 SAVEPOINT 類型的對象,主要屬性如下:

  • prev:指向 server 層的 savepoint 鏈表中,上一次創建的 SAVEPOINT 對象。
  • name:savepoint 的名字。
  • mdl_savepoint:創建這個 savepoint 之前,事務加了哪些 MDL 鎖。

binlog 的 savepoint 結構很簡單,是一個 8 字節的整數。這個整數的值,是創建 savepoint 時事務已經產生的 binlog 日誌的字節數,也是接下來新產生的 binlog 日誌寫入 trx_cache 的 offset。

爲了方便介紹,我們把這個整數值稱爲 binlog offset

InnoDB 的 savepoint 結構是一個 trx_named_savept_t 類型的對象,主要屬性如下:

  • name:InnoDB 的 savepoint 名字。這個名字是 InnoDB 自己生成的,和 server 層的 SAVEPOINT 對象中保存的 savepoint 名字不一樣。
  • savept:也是一個對象,類型爲 trx_savept_t,裏面保存着創建 savepoint 時,事務對象的 undo_no 屬性值。
  • trx_savepoints:InnoDB 中多個 trx_named_savept_t 對象形成的鏈表。

創建 savepoint 時,server 層會分配一塊 96 字節的內存,除了存放它自己的 SAVEPOINT 對象,還會存放 binlog offset 和 InnoDB 的 trx_named_savept_t 對象。

server 層的 SAVEPOINT 對象佔用這塊內存的前 48 字節,InnoDB 的 trx_named_savept_t 對象佔用中間的 40 字節,binlog offset 佔用最後的 8 字節。

3. 查找同名 savepoint

客戶端連接到 MySQL 之後,MySQL 會分配一個專門用於該連接的用戶線程。

用戶線程中有一個 m_savepoints 鏈表,用戶創建的多個 savepoint 通過 prev 屬性形成鏈表,m_savepoints 就指向最新創建的 savepoint。

server 層創建 savepoint 之前,會按照創建時間從新到老,逐個查看鏈表中是否存在和本次創建的 savepoint 同名的 savepoint。

4. 刪除同名 savepoint

如果在用戶線程的 m_savepoints 鏈表中找到了和本次創建的 savepoint 同名的 savepoint,需要先刪除 m_savepoints 鏈表中的同名 savepoint。

找到的同名 savepoint,是 server 層的 SAVEPOINT 對象,它後面的內存區域分別保存着 InnoDB 的 trx_named_savept_t 對象、binlog offset。

binlog 是個老實孩子,乖乖的把 binlog offset 寫入了 server 層爲它分配的內存裏。刪除同名 savepoint 時,不需要單獨處理 binlog offset。

InnoDB 就不老實了,雖然 server 層也爲 InnoDB 的 trx_named_savept_t 對象分配了內存,但是 InnoDB 並沒有往裏面寫入內容。

事務執行過程中,用戶每次創建一個 savepoint,InnoDB 都會創建一個對應的 trx_named_savept_t 對象,並加入 InnoDB 事務對象的 trx_savepoints 鏈表的末尾

因爲 InnoDB 自己維護了一個存放 savepoint 結構的鏈表,server 層刪除同名 savepoint 時,InnoDB 需要找到這個鏈表中對應的 savepoint 結構並刪除,流程如下:

  • server 層把同名 savepoint 的 SAVEPOINT 對象後面分配給 trx_named_savept_t 對象的內存地址傳給 InnoDB。
  • InnoDB 根據自己的算法把內存地址轉換爲字符串,作爲 InnoDB 的 savepoint 名字,到事務對象的 trx_savepoints 鏈表中找到對應的 trx_named_savept_t 對象,並從鏈表中刪除該對象。

InnoDB 從事務對象的 trx_savepoints 鏈表中刪除 trx_named_savept_t 對象之後,server 層接着從用戶線程的 m_savepoints 鏈表中刪除 server 層的 SAVEPOINT 對象,也就連帶着清理了 binlog offset

5. 保存 savepoint

處理完查找、刪除同名 savepoint 之後,server 層就正式開始創建 savepoint 了,這個過程分爲 3 步。

第 1 步,binlog 會生成一個 Query_log_event。

以創建名爲 test_savept 的 savepoint 爲例,這個 event 的內容如下:

SAVEPOINT `test_savept`

binlog event 寫入 trx_cache 之後,binlog offset 會寫入 server 層爲它分配的 8 字節的內存中。

第 2 步,InnoDB 創建 trx_named_savept_t 對象,並放入事務對象的 trx_savepoints 鏈表的末尾

trx_named_savept_t 對象的 name 屬性值是 InnoDB 的 savepoint 名字。這個名字是根據 server 層爲 InnoDB 的 trx_named_savept_t 對象分配的內存的地址計算得到的。

trx_named_savept_t 對象的 savept 屬性,是一個 trx_savept_t 類型的對象。這個對象裏保存着創建 savepoint 時,事務對象中 undo_no 屬性的值,也就是下一條 undo 日誌的序號。

第 3 步,把 server 層的 SAVEPOINT 對象加入用戶線程的 m_savepoints 鏈表的尾部。

6. 總結

server 層會創建一個 SAVEPOINT 對象,用於存放 savepoint 信息。

binlog 會把 binlog offset 寫入 server 層爲它分配的一塊 8 字節的內存裏。

InnoDB 會維護自己的 savepoint 鏈表,裏面保存着 trx_named_savept_t 對象。

如果 m_savepoints 鏈表中存在和本次創建的 savepoint 同名的 savepoint, 創建新的 savepoint 之前,server 層會從鏈表中刪除這個同名的 savepoint。

server 層創建的 SAVEPOINT 對象會放入 m_savepoints 鏈表的末尾。

InnoDB 創建的 trx_named_savept_t 對象會放入事務對象的 trx_savepoints 鏈表的末尾。

本期問題:創建 savepoint 時,爲什麼要把 SAVEPOINT xxx 寫入 trx_cache 並最終寫入 binlog 日誌文件呢?這個問題我還沒有答案,歡迎大家在留言區留下你的想法。

下期預告:MySQL 核心模塊揭祕 | 13 期 | 回滾到 savepoint。

更多技術文章,請訪問:https://opensource.actionsky.com/

關於 SQLE

SQLE 是一款全方位的 SQL 質量管理平臺,覆蓋開發至生產環境的 SQL 審覈和管理。支持主流的開源、商業、國產數據庫,爲開發和運維提供流程自動化能力,提升上線效率,提高數據質量。

SQLE 獲取

類型 地址
版本庫 https://github.com/actiontech/sqle
文檔 https://actiontech.github.io/sqle-docs/
發佈信息 https://github.com/actiontech/sqle/releases
數據審覈插件開發文檔 https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章