动态

详情 返回 返回

MySQL 核心模塊揭秘 | 05 期 | 讀事務和只讀事務的變形記 - 动态 详情

事務都以讀事務身份啓動,讀事務和只讀事務會在需要時發生變化,它們會怎麼變化?這是本文要回答的問題。

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

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

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

1. update、delete

後面小節的內容和 update、delete 有關,我們先簡單介紹一下這兩類 SQL 語句的執行流程。

以更新一條記錄為例,update 語句的簡化執行流程如下:

  • server 層要求 InnoDB 從表中讀取一條記錄。
  • InnoDB 返回記錄之後,server 層判斷這條記錄是否匹配 where 條件。
  • 如果匹配,用 update 語句 set 子句中指定的各字段值,替換 InnoDB 返回記錄的對應字段值。
  • 替換字段值得到完整記錄之後,server 層觸發 InnoDB 更新記錄。

以刪除一條記錄為例,delete 語句的簡化執行流程如下:

  • server 層要求 InnoDB 從表中讀取一條記錄。
  • InnoDB 返回記錄之後,server 層判斷這條記錄是否匹配 where 條件。
  • 如果匹配,server 層觸發 InnoDB 刪除記錄。

2. 讀事務

上一期我們介紹過,事務真正啓動於執行第一條 SQL 語句時,如果第一條 SQL 語句是 select、update、delete,事務會以讀事務的身份啓動。

讀事務啓動時,沒有分配事務 ID 和回滾段,事務對象也沒有加入到 trx_sys->rw_trx_list 鏈表。

根據我們使用 MySQL 的經驗,以讀事務身份啓動的事務,不僅能正常執行改變(插入、更新、刪除)表中數據的操作,還支持 MVCC、回滾。

對於啓動時沒有分配事務 ID 和回滾段的讀事務來説,這是怎麼做到的呢?

有一句話能夠很好的回答這個問題,就是以發展的眼光看問題

以讀事務身份啓動的事務,並不意味着一直都是讀事務,它可以在某些時間點變成讀寫事務

根據執行的第一條 SQL 語句不同,讀事務變成讀寫事務的時間點可以分為兩類:

第一類:第一條 SQL 語句是 update 或 delete。

在 update 或 delete 語句執行過程中,讀事務就會變成讀寫事務。

發生變化的具體時間點,又取決於這兩類 SQL 語句更新或刪除記錄的第一個表是什麼類型。

如果第一個表是用户普通表,InnoDB 從表中讀取一條記錄之前,會給表加意向排他鎖(IX)。

加意向排他鎖時,如果以下三個條件成立,InnoDB 就會把這個事務變成讀寫事務:

  • 事務還沒有為用户普通表分配回滾段。
  • 事務 ID 為 0,説明這個事務現在還是讀事務。
  • 事務的只讀標識 trx->read_only = false,説明這個事務可以變成讀寫事務。

讀事務變成讀寫事務,InnoDB 主要做 3 件事:

  • 分配事務 ID。
  • 為用户普通表分配回滾段。
  • 把事務對象加入 trx_sys->rw_trx_list 鏈表。

如果第一個表是用户臨時表,因為它的可見範圍只限於創建這個表的數據庫連接之內,其它數據庫連接中執行的事務都看不到這個表,更不能改變表中的數據,所以,update、delete 語句改變用户臨時表中的數據,不需要加意向排他鎖。

讀事務變成讀寫事務的操作會延遲到 server 層觸發 InnoDB 更新或刪除記錄之後,InnoDB 執行更新或刪除操作之前。

在這個時間節點,如果以下三個條件成立,InnoDB 就會把這個事務變成讀寫事務:

  • 事務已經啓動了。
  • 事務 ID 為 0,説明這個事務現在還是讀事務。
  • 事務的只讀標識 trx->read_only = false,説明這個事務可以變成讀寫事務。

有一點需要説明,改變用户臨時表的數據觸發讀事務變成讀寫事務,不會分配用户臨時表回滾段,需要等到為某個用户臨時表第一次寫 Undo 日誌時才分配。

第二類:第一條 SQL 語句是 select。

在 select 語句執行過程中,讀事務不會變成讀寫事務;這條 SQL 語句執行完之後、事務提交之前,第一次執行 insert、update、delete 語句時,讀事務才會變成讀寫事務。

一個讀事務變成讀寫事務的操作,只會發生一次,發生變化的具體時間點,取決於最先碰到哪種 SQL 語句。

如果最先碰到 insert 語句,server 層準備好要插入的記錄的每個字段之後,會觸發 InnoDB 執行插入操作。

執行插入操作之前,如果以下三個條件成立,InnoDB 就會把這個事務變成讀寫事務:

  • 事務已經啓動了。
  • 事務 ID 為 0,説明這個事務現在還是讀事務。
  • 事務的只讀標識 trx->read_only = false,説明這個事務可以變成讀寫事務。

如果最先碰到的是 update 或 delete 語句,讀事務變成讀寫事務的具體時間點,參照第一類中關於用户普通表、用户臨時表的介紹。

3. 只讀事務

只讀事務是讀事務的特例,以 start transaction 開始一個事務時,如果包含了 read only 關鍵字,這個事務就是一個只讀事務。例如:

start transaction read only

只讀事務不能改變(插入、更新、刪除)系統表、用户普通表的數據,但是能改變用户臨時表的數據。

改變用户臨時表的數據,同樣需要為事務分配事務 ID,為用户臨時表分配回滾段。根據只讀事務執行的第一條 SQL 語句不同,這兩個操作發生的時間點也可以分為兩類。

第一類:第一條 SQL 語句是 update 或 delete。

在 update 或 delete 語句執行過程中,server 層觸發 InnoDB 更新或刪除記錄之後,InnoDB 執行更新或刪除操作之前,如果以下三個條件成立,InnoDB 就為這個事務分配事務 ID、為用户臨時表分配回滾段:

  • 事務已經啓動了。
  • 事務 ID 為 0。
  • 事務是個只讀事務(trx->read_only = true)。

第二類:第一條 SQL 語句是 select。

在 select 語句執行過程中,不會分配事務 ID 和用户臨時表的回滾段;這條 SQL 執行完之後、事務提交之前,第一次執行 insert、update、delete 語句時,才會執行這兩個操作。

對於一個只讀事務,這兩個操作只會執行一次,執行的具體時間點,取決於最先碰到哪種 SQL 語句。

如果最先碰到 insert 語句,server 層準備好要插入的記錄的每個字段之後,會觸發 InnoDB 執行插入操作。

執行插入操作之前,如果以下三個條件成立,InnoDB 會為這個只讀事務分配事務 ID、為用户臨時表分配回滾段:

  • 事務已經啓動了。
  • 事務 ID 為 0。
  • 事務是個只讀事務(trx->read_only = true)。

如果最先碰到的是 update 或 delete 語句,執行這兩個操作的具體時間點,參照第一類的介紹。

4. 總結

以讀事務或只讀事務身份啓動的事務:

  • 如果執行的第一條 SQL 語句是 update 或 delete,在 SQL 語句執行過程中,讀事務會變成讀寫事務,只讀事務會分配事務 ID 和用户臨時表的回滾段。
  • 如果執行的第一條 SQL 語句是 select,在後續第一次執行 insert、update、delete 三種語句的其中一種時,讀事務會變成讀寫事務,只讀事務會分配事務 ID 和用户臨時表的回滾段。

讀事務變成讀寫事務,InnoDB 主要做 3 件事:

  • 分配事務 ID。
  • 為用户普通表分配回滾段。
  • 把事務對象加入 trx_sys->rw_trx_list 鏈表。

本期問題:關於本期內容,如有問題,歡迎留言交流。

下期預告:MySQL 核心模塊揭秘 | 06 期 | 事務提交之前,binlog 寫到哪裏?

user avatar xiaofeixiang_63ec941cad48a 头像
点赞 1 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.