Mysql學習筆記 事務的隔離

2021-10-12 12:37:14 字數 2676 閱讀 3137

可重複讀隔離級別的理解,事務 t 啟動的時候會建立乙個檢視 read-view,之後事務 t 執行期間,即使有其他事務修改了資料,事務 t 看到的仍然跟在啟動時看到的一樣。也就是說,乙個在可重複讀隔離級別下執行的事務,好像與世無爭,不受外界影響。

這個技術在mvcc中或許可以是這樣理解的。但是如果是在通過鎖技術實現隔離的場景中,事務t啟動的時候就不是建立乙個檢視了,而且給讀取到的某個區域的資料全部加行鎖,並在資料行之間加gap鎖,以保證其他事務無法修改被鎖住資料行之間的全部資料。

begin/start transaction 命令並不是乙個事務的起點,在執行到它們之後的第乙個操作 innodb 表的語句,事務才真正啟動。如果你想要馬上啟動乙個事務,可以使用 start transaction with consistent snapshot 這個命令。

在 mysql 裡,有兩個「檢視」的概念:

乙個是 view。它是乙個用查詢語句定義的虛擬表,在呼叫的時候執行查詢語句並生成結果。建立檢視的語法是 create view … ,而它的查詢方法與表一樣。

另乙個是 innodb 在實現 mvcc 時用到的一致性讀檢視,即 consistent read view,用於支援 rc(read committed,讀提交)和 rr(repeatable read,可重複讀)隔離級別的實現。

在可重複讀隔離級別下,事務在啟動的時候就「拍了個快照」。注意,這個快照是基於整庫的。

innodb 裡面每個事務有乙個唯一的事務 id,叫作 transaction id。它是在事務開始的時候向 innodb 的事務系統申請的,是按申請順序嚴格遞增的。

而每行資料也都是有多個版本的。每次事務更新資料的時候,都會生成乙個新的資料版本,並且把 transaction id 賦值給這個資料版本的事務 id,記為 row trx_id。同時,舊的資料版本要保留,並且在新的資料版本中,能夠有資訊可以直接拿到它。

也就是說,資料表中的一行記錄,其實可能有多個版本 (row),每個版本有自己的 row trx_id。

圖中虛線框裡是同一行資料的 4 個版本,當前最新版本是 v4,k 的值是 22,它是被 transaction id 為 25 的事務更新的,因此它的 row trx_id 也是 25。

你可能會問,前面的文章不是說,語句更新會生成 undo log(回滾日誌)嗎?那麼,undo log 在哪呢?

實際上,圖 2 中的三個虛線箭頭,就是 undo log;而 v1、v2、v3 並不是物理上真實存在的,而是每次需要的時候根據當前版本和 undo log 計算出來的。比如,需要 v2 的時候,就是通過 v4 依次執行 u3、u2 算出來。

按照可重複讀的定義,乙個事務啟動的時候,能夠看到所有已經提交的事務結果。但是之後,這個事務執行期間,其他事務的更新對它不可見。

因此,乙個事務只需要在啟動的時候宣告說,「以我啟動的時刻為準,如果乙個資料版本是在我啟動之前生成的,就認;如果是我啟動以後才生成的,我就不認,我必須要找到它的上乙個版本」。

當然,如果「上乙個版本」也不可見,那就得繼續往前找。還有,如果是這個事務自己更新的資料,它自己還是要認的。

在實現上, innodb 為每個事務構造了乙個陣列,用來儲存這個事務啟動瞬間,當前正在「活躍」的所有事務 id。「活躍」指的就是,啟動了但還沒提交。陣列裡面事務 id 的最小值記為低水位,當前系統裡面已經建立過的事務 id 的最大值加 1 記為高水位。這個檢視陣列和高水位,就組成了當前事務的一致性檢視(read-view)。

對於當前事務的啟動瞬間來說,乙個資料版本的 row trx_id,有以下幾種可能:

如果落在綠色部分,表示這個版本是已提交的事務或者是當前事務自己生成的,這個資料是可見的;

如果落在紅色部分,表示這個版本是由將來啟動的事務生成的,是肯定不可見的;

如果落在黃色部分,那就包括兩種情況:

a. 若 row trx_id 在陣列中,表示這個版本是由還沒提交的事務生成的,不可見;

b. 若 row trx_id 不在陣列中,表示這個版本是已經提交了的事務生成的,可見。

乙個資料版本,對於乙個事務檢視來說,除了自己的更新總是可見以外,有三種情況:

版本未提交,不可見;

版本已提交,但是是在檢視建立後提交的,不可見;

版本已提交,而且是在檢視建立前提交的,可見。

可重複讀的核心就是一致性讀(consistent read);而事務更新資料的時候,只能用當前讀。如果當前的記錄的行鎖被其他事務占用的話,就需要進入鎖等待。而讀提交的邏輯和可重複讀的邏輯類似,它們最主要的區別是:

在可重複讀隔離級別下,只需要在事務開始的時候建立一致性檢視,之後事務裡的其他查詢都共用這個一致性檢視;

在讀提交隔離級別下,每乙個語句執行前都會重新算出乙個新的檢視。

「start transaction with consistent snapshot; 」的意思是從這個語句開始,建立乙個持續整個事務的一致性快照。所以,在讀提交隔離級別下,這個用法就沒意義了,等效於普通的 start transaction。

MySQL學習筆記 事務隔離與鎖

可重複讀級別下,在事務執行期間,即使其他事務修改了資料,該事務看到的仍然跟啟動是看到的一樣。但是,如果乙個事務要更新一行資料,這時剛好有另乙個事務擁有這一行的行鎖,那本事務就會進入鎖等待狀態。問題 該事務獲取到行鎖,要更新資料時,這時候,它讀的資料值是什麼呢?答 更新資料的時候,都是先讀後寫,這個讀...

MySQL學習筆記 事務和隔離級別

在資料庫操作中,為了有效保證併發讀取資料的正確性,提出的事務隔離級別。我們的資料庫鎖,也是為了構建這些隔離級別存在的。總共有4種 1.未提交讀 read uncommitted 允許髒讀,也就是可能讀取到其他會話中未提交事務修改的資料 2.提交讀 read committed 只能讀取到已經提交的資...

MySQL學習筆記 事務

事務是用來保證一組資料庫的操作,要麼全部成功,要麼全部失敗 應用場景較多 如銀行轉賬,訂票等。mysql的事務是在引擎層支援的,原生的myisam不支援,因此主流使用innodb引擎。原子性顧名思義,不可分割,要麼所有指令都成功,要麼所有指令都失敗 一致性事務開始前和事務結束後,資料庫的狀態都是正常...