動態

詳情 返回 返回

MySQL 核心模塊揭秘 | 03 期 | 我是一個事務,請給我一個對象 - 動態 詳情

每個事務都有一個對象,這篇文章我們聊聊,事務的對象從哪裏來,要到哪裏去。

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

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

我是一個事務,請給我一個對象

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

目錄
[TOC]

正文

1. 用户事務和內部事務

InnoDB 讀寫表中數據的操作都在事務中執行,開始一個事務的方式有兩種:

  • 手動:通過 BEGINSTART TRANSACTION 語句以及它們的擴展形式開始一個事務。
  • 自動:直接執行一條 SQL 語句,InnoDB 會自動開始一個事務,SQL 語句執行完成之後,又會自動提交這個事務。

這兩種方式開始的事務,都用來執行用户 SQL 語句,屬於用户事務

InnoDB 有時候也需要自己執行一些 SQL 語句,為了和用户 SQL 做區分,我們把這些 SQL 稱為內部 SQL。

內部 SQL 也需要在事務中執行,執行這些 SQL 的事務就是內部事務

InnoDB 有幾種場景會使用內部事務,以下是其中主要的三種:

  • 如果上次關閉 MySQL 時有未提交,或者正在提交但未提交完成的事務,啓動過程中,InnoDB 會把這些事務恢復為內部事務,然後提交或者回滾。
  • 後台線程執行一些操作時,需要在內部事務中執行內部 SQL。

    以 ib_dict_stats 線程為例,它計算各表、索引的統計信息之後,會使用內部事務執行內部 SQL,更新 mysql.innodb_table_stats、mysql.innodb_index_stats 表中的統計信息。
  • 為了實現原子操作,DDL 語句執行過程中,InnoDB 會使用內部事務執行內部 SQL,插入一些數據到 mysql.innodb_ddl_log 表中。

2. 分配事務對象

InnoDB 用事務池來管理事務對象,用事務池管理器來管理事務池。

不管是用户事務,還是內部事務,真正啓動事務之前,都需要通過事務池管理器從某個事務池的事務隊列中分配一個事務對象。

已經創建的那些事務池,都放在事務池管理器的 m_pools 數組中。分配事務對象時,先從第 1 個事務池開始,過程是這樣的:

  • 如果事務池的事務隊列中有可用的事務對象,直接分配一個就好了。
  • 否則,看看事務池中還有沒有未初始化的小塊內存。
  • 如果有,那就好辦了,把這些小塊內存全部初始化,得到的事務對象都放入該事務池的事務隊列,並從中分配一個事務對象。
  • 否則,繼續對下一個事務池,走一遍上面的流程。
  • 要是沒有下一個事務池,怎麼辦?
  • 也好辦,那就創建一個新事務池,初始化之後,就可以直接從它的事務隊列中分配一個事務對象了。

3. 再做一些初始化工作

分配一個事務對象,得到的是一個出廠設置的對象,這個對象的各屬性值都已經是初始狀態了。

分配事務對象之後,InnoDB 還會對事務對象的幾個屬性再做一次初始化工作,把這幾個屬性再一次設置為初始值,其實就是對這些屬性做了重複的賦值操作。

這些屬性中,有必要提一下的是事務狀態(trx->state)。出廠設置的事務對象,事務狀態是 TRX_STATE_NOT_STARTED,表示事務還沒有開始。

我們執行 show engine innodb status 可能會看到類似下面的內容:

LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 281480261177256, not started
0 lock struct(s), heap size 1192, 0 row lock(s)

其中,not started 就來源於事務的 TRX_STATE_NOT_STARTED 狀態。

除了給幾個屬性重複賦值,還會改變另外兩個屬性的值:

  • trx->in_innodb:給這個屬性值加上 TRX_FORCE_ROLLBACK_DISABLE 標誌,防止這個事務被其它線程觸發回滾操作。事務後續執行過程中,這個標誌可能會被清除,我們就不展開介紹了。
  • trx->lock.autoinc_locks:分配一塊內存空間,用於存放 autoinc 鎖結構。事務執行過程中需要為 auto_increment 字段生成自增值時使用。

4. 加入事務鏈表

我們查詢 information_schema.innodb_trx 表,能看到當前正在執行的事務有哪些,這些事務來源於兩個鏈表。

為用户事務分配一個事務對象之後,還有一件非常重要的事,就是把事務對象放入其中一個鏈表的最前面,代碼是這樣的:

UT_LIST_ADD_FIRST(trx_sys->mysql_trx_list, trx);

從上面的代碼可以看到,這個鏈表就是 trx_sys->mysql_trx_list,它只會記錄用户事務。

至於內部事務,並不會放入 trx_sys->mysql_trx_list 鏈表。等到真正啓動事務時,事務對象會被放入另一個鏈表,我們先按下不表,留個懸念,後面的內容會介紹。

5. 總結

InnoDB 把事務分為用户事務和內部事務,給事務分配對象時,會按照這個順序:

  • 先從事務池的事務隊列中分配一個對象。
  • 如果事務隊列中沒有可用的事務對象,就初始化事務池的剩餘小塊內存,從得到的事務對象中分配一個對象。
  • 如果所有事務池都沒有剩餘未初始化的小塊內存,就創建一個新的事務池,並從中分配一個事務對象。

本期問題:InnoDB 怎麼沒有把內部事務也放入 trx_sys->mysql_trx_list 鏈表?歡迎大家留言交流。

下期預告:準備那麼久,終於要啓動 InnoDB 事務了。

user avatar u_13482808 頭像 zjkal 頭像 renxingdebenma 頭像 yishenjiroudekaixinguo 頭像 danieldx 頭像 nianqingyouweideyizi 頭像 motianlun_5d0766992e67a 頭像
點贊 7 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.