可重复读:为什么你改了我看不到呢?
在探索問題之前,先得明白如下知識點
- InnoDB 里面每個事務有一個唯一的事務 ID,叫作 transaction id。它是在事務開始的時候向 InnoDB 的事務系統申請的,是按申請順序嚴格遞增的。
- 而每行數據也都是有多個版本的。每次事務更新數據的時候,都會生成一個新的數據版本,并且 把 transaction id 賦值給這個數據版本的事務 ID,記為 row trx_id。
還沒懂的話 就看下面這張圖,理解一下
開始探索問題
按照可重復讀的定義,一個事務啟動的時候,能夠看到所有已經提交的事務結果。但是之后,這 個事務執行期間,其他事務的更新對它不可見。 因此,InnoDB 代碼實現上,一個事務只需要在啟動的時候,找到所有已經提交的事務 ID 的最 大值,記為 up_limit_id;然后聲明說,“如果一個數據版本的 row trx_id 大于 up_limit_id,我就不認,我必須要找到它的上一個版本”。當然,如果一個事務自己更新的數據,它自己還是 要認的。有了這個聲明后,系統里面隨后發生的更新,是不是就跟這個事務看到的內容無關了呢? 因為之后的更新,產生的新的數據版本的 row trx_id 都會大于 up_limit_id。
是不是還是有點懵,舉個栗子
我們繼續看一下圖 中的三個事務,分析下 Q2 語句返回的結果,為什么是 k=1。 這里,我們不妨做如下假設: 1. 事務 A 開始前,系統里面已經提交的事務最大 ID 是 99; 2. 事務 A、B、C 的版本號分別是 100、101、102,且當前系統里沒有別的事務; 3. 三個事務開始前,(1,1)這一行數據的 row trx_id 是 90。 這樣,事務 A、B、C 的 up_limit_id 的值就都是 99。 為了簡化分析,我先把其他干擾語句去掉,只畫出了跟 Q2 查詢邏輯有關的操作。
從圖中可以看到,第一個有效更新是事務 C,把數據從 (1,1) 改成了 (1,2)。這時候,這個數據的 最新版本的 row trx_id 是 102,而 90 這個版本已經成為了歷史版本。 第二個有效更新是事務 B,把數據從 (1,2) 改成了 (1,3)。這時候,這個數據的最新版本(即 row trx_id)是 101,而 102 又成為了歷史版本。 好,現在事務 A 要來讀數據了,它的 up_limit_id 是 99。當然了,讀數據都是從當前版本讀起 的。所以,Q2 的讀數據流程是這樣的:
這樣執行下來,事務 A 讀到的這個數據,跟它在剛開始啟動的時候讀到的相同,所以我們稱之 為一致性讀。
(1,1) 這個歷史版本,什么時候可以被刪除掉呢? 答案是,當沒有事務再需要它的時候,就可以刪掉。
同時這也強調了我們不建議使用長事務的原因,回保存多個視圖版本從而占據了大量內存。
順路再說下讀提交與可重復讀的區別
- 在可重復讀隔離級別下,只需要在事務開始的時候找到那個 up_limit_id,之后事務里的其他 查詢都共用這個 up_limit_id;
- 在讀提交隔離級別下,每一個語句執行前都會重新算一次 up_limit_id 的值。
總結
以上是生活随笔為你收集整理的可重复读:为什么你改了我看不到呢?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 行级锁
- 下一篇: 你懂change buffer吗