首先來介紹一下 MySQL 里面的“視圖”的概念。
一致性視圖工作原理通過之前的文章我們知道,在可重復(fù)讀隔離級別下,事務(wù)開始前會創(chuàng)建一個一致性視圖。下面我們來詳細(xì)說明一下這個一致性視圖的工作原理。 在InnoDB 引擎中,每個事務(wù)都有一個唯一的ID,就是 transaction id。它是在事務(wù)開始的時候向系統(tǒng)申請的,是嚴(yán)格按順序遞增的。我們知道,每個數(shù)據(jù)行都是有多個版本的。每一次的事務(wù)更新都會有一個新的版本,并且每個版本都有對應(yīng)的 transaction id(row trx_id)。 上圖是一條行數(shù)據(jù)的多個版本,最新的版本是 V4。但是需要注意的是,上圖中的 U1、U2、U3 對應(yīng)的就是 undo log (回滾日志),小版本 trx_id 的值都是通過 undo log 計算出來的。 按照 可重復(fù)讀的語義,每個事務(wù)啟動的時候只能看到已經(jīng)提交的事務(wù),并且在本事務(wù)執(zhí)行的過程中,不可以讀取到其他事務(wù)的更新操作。在InnoDB 中,為每個事務(wù)構(gòu)造了一個 當(dāng)前事務(wù)ID數(shù)組 的快照,就是記錄事務(wù)開啟時,當(dāng)前正在執(zhí)行的事務(wù)ID 的集合。數(shù)組里面 trx_id 最小的記為 低水位,trx_id 最大的 + 1 記為高水位。如下圖所示: 對于一個新事務(wù)而言,所讀取到的記錄版本的 trx_id 可能有以下幾種情況:
對于 MVCC 的多版本圖,如果當(dāng)前有一個事務(wù),它的低水位是 18 。此時它訪問這個數(shù)據(jù)行時,會通過 V4 版本計算出 V3 的版本。由此我們可以看出,InnoDB 利用了 數(shù)據(jù)多版本的特點,實現(xiàn)了快速創(chuàng)建快照的能力。 我們下面看一個 Demo:1、事務(wù)A 開始前,系統(tǒng)中只有一個 99 的已提交事務(wù);2、事務(wù) A、B、C的版本號分別是 100、101、102,且當(dāng)前系統(tǒng)中只有這 4 個事務(wù);3、事務(wù)開始前,(1, 1) 這一行數(shù)據(jù)的 trx_id 是 90. 上圖從 事務(wù)A的可重復(fù)讀角度看來,101、102 版本都不可見,因此找到了 90 這個版本的數(shù)據(jù)。 更新操作前面一段說了,數(shù)據(jù)行多版本的讀取規(guī)則。下面說明一下,更新數(shù)據(jù)的讀取規(guī)則。如下所示: 上面 事務(wù)B 執(zhí)行的過程中,有事務(wù) C的提交流程。此時事務(wù)B 就不能按照 undo log 回滾到 90 這個版本了(如果是這樣事務(wù)B 執(zhí)行完成數(shù)據(jù)就變成 (1, 2)了,與正確的結(jié)果 (1, 3) 不符)。此時更新的讀取為“當(dāng)前讀”,也就是讀取到最新的數(shù)據(jù),事務(wù)B 執(zhí)行完 k=k+1 后,再次獲取 k 的值時,返回的就是 (1, 3) 了。 上面的過程,我們看到的是 事務(wù)C 在事務(wù)B 執(zhí)行更新之前就已經(jīng)提交了。如果事務(wù)C 沒有提交,那又會是一個什么流程呢? 從上圖可以看出,如果 事務(wù)C' 沒有提交,那么事務(wù)B 的更新就會等待,知道事務(wù)C' 執(zhí)行完成。 可重復(fù)讀和讀已提交的區(qū)別在可重復(fù)讀隔離級別下,需要在事務(wù)開啟前創(chuàng)建一個一致性視圖。而讀已提交,則是每執(zhí)行一個語句前都會創(chuàng)建一個新的視圖。 參考:《極客時間:MySQL實戰(zhàn)》、《高性能MySQL》 |
|