MySQL事務隔離原理

首先來介紹一下 MySQL 裡面的“視圖”的概念。

  • 視圖:查詢語句定義的虛擬表,可以通過 create view ... 來創建。
  • 一致性視圖:InnoDB 實現的,在 MVCC 中用到的,用於支持 RC (Read Commited,讀提交) 和 RR (Repeatable Read,可重複讀)隔離級別的實現。

一致性視圖工作原理

通過之前的文章我們知道,在可重複讀隔離級別下,事務開始前會創建一個一致性視圖。下面我們來詳細說明一下這個一致性視圖的工作原理。

在InnoDB 引擎中,每個事務都有一個唯一的ID,就是 transaction id。它是在事務開始的時候向系統申請的,是嚴格按順序遞增的。我們知道,每個數據行都是有多個版本的。每一次的事務更新都會有一個新的版本,並且每個版本都有對應的 transaction id(row trx_id)。

MySQL事務隔離原理

上圖是一條行數據的多個版本,最新的版本是 V4。但是需要注意的是,上圖中的 U1、U2、U3 對應的就是 undo log (回滾日誌),小版本 trx_id 的值都是通過 undo log 計算出來的。

按照 可重複讀的語義,每個事務啟動的時候只能看到已經提交的事務,並且在本事務執行的過程中,不可以讀取到其他事務的更新操作。在InnoDB 中,為每個事務構造了一個 當前事務ID數組 的快照,就是記錄事務開啟時,當前正在執行的事務ID 的集合。數組裡面 trx_id 最小的記為 低水位,trx_id 最大的 + 1 記為高水位。如下圖所示:

MySQL事務隔離原理

對於一個新事務而言,所讀取到的記錄版本的 trx_id 可能有以下幾種情況:

  • 在綠色區域:說明數據版本在事務開始前已提交,當前版本是可見的。
  • 在紅色區域:說明數據版本在事務開始後變更的,當前版本是不可見的。
  • 在橙色區域:包含 2 種情況。
  1. 如果 數據版本的 trx_id 在數組中,說明是正在執行的事務,不可見。
  2. 如果 數據版本的 trx_id 不在數組中,說明是已經提交的事務,可見。

對於 MVCC 的多版本圖,如果當前有一個事務,它的低水位是 18 。此時它訪問這個數據行時,會通過 V4 版本計算出 V3 的版本。由此我們可以看出,InnoDB 利用了 數據多版本的特點,實現了快速創建快照的能力。

我們下面看一個 Demo:1、事務A 開始前,系統中只有一個 99 的已提交事務;2、事務 A、B、C的版本號分別是 100、101、102,且當前系統中只有這 4 個事務;3、事務開始前,(1, 1) 這一行數據的 trx_id 是 90.

MySQL事務隔離原理

上圖從 事務A的可重複讀角度看來,101、102 版本都不可見,因此找到了 90 這個版本的數據。

更新操作

前面一段說了,數據行多版本的讀取規則。下面說明一下,更新數據的讀取規則。如下所示:

MySQL事務隔離原理

上面 事務B 執行的過程中,有事務 C的提交流程。此時事務B 就不能按照 undo log 回滾到 90 這個版本了(如果是這樣事務B 執行完成數據就變成 (1, 2)了,與正確的結果 (1, 3) 不符)。此時更新的讀取為“當前讀”,也就是讀取到最新的數據,事務B 執行完 k=k+1 後,再次獲取 k 的值時,返回的就是 (1, 3) 了。

上面的過程,我們看到的是 事務C 在事務B 執行更新之前就已經提交了。如果事務C 沒有提交,那又會是一個什麼流程呢?

MySQL事務隔離原理

從上圖可以看出,如果 事務C' 沒有提交,那麼事務B 的更新就會等待,知道事務C' 執行完成。

可重複讀和讀已提交的區別

在可重複讀隔離級別下,需要在事務開啟前創建一個一致性視圖。而讀已提交,則是每執行一個語句前都會創建一個新的視圖。

參考:《極客時間:MySQL實戰》、《高性能MySQL》


分享到:


相關文章: