08 | 事务到底是隔离的还是不隔离的
??如果是可重復讀隔離級別,事務 T 啟動的時候會創建一個視圖 read-view,之后事務 T 執行期間,即使有其他事務修改了數據,事務 T 看到的仍然跟在啟動時看到的一樣。也就是說,一個在可重復讀隔離級別下執行的事務,好像與世無爭,不受外界影響。
??分享行鎖的時候又提到,一個事務要更新一行,如果剛好有另外一個事務擁有這一行的行鎖,它又不能這么超然了,會被鎖住,進入等待狀態。問題是,既然進入了等待狀態,那么等到這個事務自己獲取到行鎖要更新數據的時候,它讀到的值又是什么呢?
begin/start transaction 命令并不是一個事務的起點,在執行到它們之后的第一個操作InnoDB 表的語句,事務才真正啟動。如果你想要馬上啟動一個事務,可以使用starttransaction with consistent snapshot 這個命令。
- 第一種啟動方式,一致性視圖是在第執行第一個快照讀語句時創建的;
- 第二種啟動方式,一致性視圖是在執行 start transaction with consistent snapshot 時創建的。
??在這個例子中,事務 C 沒有顯式地使用 begin/commit,表示這個 update 語句本身就是一個事務,語句完成的時候會自動提交。事務 B 在更新了行之后查詢 ; 事務 A 在一個只讀事務中查詢,并且時間順序上是在事務 B 的查詢之后。
在 MySQL 里,有兩個“視圖”的概念:
- 一個是 view。它是一個用查詢語句定義的虛擬表,在調用的時候執行查詢語句并生成結果。創建視圖的語法是 create view … ,而它的查詢方法與表一樣。
- 另一個是 InnoDB 在實現 MVCC 時用到的一致性讀視圖,即 consistent read view,用于支持 RC(Read Committed,讀提交)和 RR(Repeatable Read,可重復讀)隔離級別的實現。
快照在MVCC里是怎么工作的?
??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 算出來。
??這個視圖數組把所有的 row trx_id 分成了幾種不同的情況。
這樣,對于當前事務的啟動瞬間來說,一個數據版本的 row trx_id,有以下幾種可能:
a. 若 row trx_id 在數組中,表示這個版本是由還沒提交的事務生成的,不可見;
b. 若 row trx_id 不在數組中,表示這個版本是已經提交了的事務生成的,可見。
所以你現在知道了,InnoDB 利用了“所有數據都有多個版本”的這個特性,實現了“秒級創建快照”的能力。
接下來,我們繼續看一下圖 1 中的三個事務,分析下事務 A 的語句返回的結果,為什么是k=1。這里,我們不妨做如下假設:
??這樣,事務 A 的視圖數組就是 [99,100], 事務 B 的視圖數組是 [99,100,101], 事務 C 的視圖數組是 [99,100,101,102]。
為了簡化分析,我先把其他干擾語句去掉,只畫出跟事務 A 查詢邏輯有關的操作:
更新邏輯
??事務 B 的 update 語句,如果按照一致性讀,好像結果不對
哦?
你看圖 5 中,事務 B 的視圖數組是先生成的,之后事務 C 才提交,不是應該看不見 (1,2)嗎,怎么能算出 (1,3) 來?
所以,這里就用到了這樣一條規則:
更新數據都是先讀后寫的,而這個讀,只能讀當前的
值,稱為“當前讀”(current read)。
??因此,在更新的時候,當前讀拿到的數據是 (1,2),更新后生成了新版本的數據 (1,3),這個新版本的 row trx_id 是 101。
總結
以上是生活随笔為你收集整理的08 | 事务到底是隔离的还是不隔离的的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 07丨行锁功过:怎么减少行锁对性能的影响
- 下一篇: Tomcat9URL不支持特殊字符解决方