三分鐘小短文:一致性非鎖定讀與一致性鎖定讀

台上三分鐘,台下三小時,兄弟們,今天咱們花三分鐘了解下數據庫中的兩種讀(select)操作:一致性非鎖定讀一致性鎖定讀

一致性非鎖定讀

一致性非鎖定讀是什麼?這裡我先給出一個最最最簡單的解釋:一致性非鎖定讀就是讀快照

快照即當前行數據之前的歷史版本,每行記錄可能存在多個歷史版本,或者說每行記錄可能有不止一個快照數據,一般我們將這種技術稱為 行多版本技術。而由於一個行記錄可能對應着多個快照(歷史版本),為此不可避免地會帶來一系列的並發問題,如何解決這些並發問題,就是所謂的 多版本並發控制(MVCC),當然,這不是本文的重點。

在不同事務隔離級別下,讀取的方式不同。只有在事務隔離級別 READ COMMITTED 和 REPEATABLE READ(默認)下,InnoDB 存儲引擎才會使用非鎖定的一致性讀。並且,即使都是使用非鎖定的一致性讀,它倆對於快照數據的定義也各不相同

  • 在 READ COMMITTED 事務隔離級別下,總是讀取行的最新版本;如果行被鎖定了,非一致性讀不會因此去等待行上鎖的釋放,而是去讀取該行版本的最新一個快照,如下圖所示:

    img

  • 在 REPEATABLE READ 事務隔離級別下,對於快照數據,非一致性讀總是讀取事務開始時的行數據版本

這麼說可能還不是很好理解,舉個例子,這個時候又得掏出我們經典的 user 表了(滑稽),表中包含三個字段 id、username、age,已存在一行記錄:

id = 1, username = 'Jack', age = 20;

1)第一步,我們開啟一個事務,執行如下語句:

事務 1:
	begin;
	select * from user where id = 1;

2)可以看到,第一個事務並沒有提交,這時,我們開啟第二個事務模擬並發,執行如下語句:

事務 2:
	begin;
	update user set id = 100 where id = 1;

3)在第二個事務中,將表中 id 為 1 的記錄修改為了 id=100,但是事務同樣沒有提交(即此時 id = 1 的行記錄被事務 2 加上了行鎖)。這時如果在第一個事務中再次讀取 id 為 1 的記錄,那顯然還是 1 對吧:

事務 1:
	select * from user where id = 1;

4)接着,我們再來提交下第 2 個事務中所作的修改:

事務 2:
	commit;

由於當前 id = 1 的數據被修改成了 100,也就是說,當前 id = 100 的行記錄擁有了一個 id = 1 的歷史版本

5)這個時候,再去事務 1 中讀取 id 為 1 的記錄,在 READ COMMITTED 和 REPEATABLE 事務隔離級別下得到結果就不一樣了:

  • 對於 READ COMMITTED 的事務隔離級別,由於事務 2 已經提交了,也就是說 id = 1 的行記錄沒有被事務 2 鎖定,所以就會去讀取該行的最新版本,即 id = 100,So, 在 READ COMMITTED 的事務隔離級別下,此時查詢 id = 1 的結果是 Empty Set;

  • 而在 REPEATABLE READ 事務隔離級別下,非一致性讀總是讀取事務開始時的行數據版本。也就是說,在事務 1 剛開始的時候,id = 1 的數據行是什麼樣,現在讀到的就是什麼樣的:

可以結合下面這張圖來回顧下上述的過程:

一致性鎖定讀

其實從名字上也能看出來,非一致性鎖定讀適用於對數據一致性要求不是很高的情況,比如在 READ COMMITTED 隔離級別下,即使行被鎖定了,非一致性讀也可以讀到該行版本的最新一個快照。也即,非鎖定讀機制極大地提高了數據庫的並發性

而一致性鎖定讀適用於對數據一致性要求比較高的情況,這個時候我們需要對讀操作進行加鎖以保證數據邏輯的一致性。

InnoDB 存儲引擎對讀操作支持兩種一致性鎖定讀方式,或者說對讀操作支持兩種加鎖方式:

  • SELECT ... FOR UPDATE,對於讀取的行記錄加一個 X 排它鎖,其他事務不能對鎖定的行加任何鎖
  • SELECT ... LOCK IN SHARE MODE,對於讀取的行記錄添加一個 S 共享鎖。其它事務可以向被鎖定的行加 S 鎖,但是不允許添加 X 鎖,否則會被阻塞住

So,如何用大白話解釋一致性鎖定讀?上面這兩條特殊的 select 語句就是一致性鎖定讀!一致性鎖定讀就是給行記錄加 X 鎖或 S 鎖!

簡單不?

🎉 關注公眾號 | 飛天小牛肉,即時獲取更新

  • 博主東南大學碩士在讀,攜程 Java 後台開發暑期實習生,利用課餘時間運營一個公眾號『 飛天小牛肉 』,2020/12/29 日開通,專註分享計算機基礎(數據結構 + 算法 + 計算機網絡 + 數據庫 + 操作系統 + Linux)、Java 技術棧等相關原創技術好文。本公眾號的目的就是讓大家可以快速掌握重點知識,有的放矢。關注公眾號第一時間獲取文章更新,成長的路上我們一起進步

  • 並推薦個人維護的開源教程類項目: CS-Wiki(Gitee 推薦項目,現已累計 1.8k+ star), 致力打造完善的後端知識體系,在技術的路上少走彎路,歡迎各位小夥伴前來交流學習 ~ 😊

  • 如果各位小夥伴春招秋招沒有拿得出手的項目的話,可以參考我寫的一個項目「開源社區系統 Echo」Gitee 官方推薦項目,目前已累計 900+ star,基於 SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + … 並提供詳細的開發文檔和配套教程。公眾號後台回復 Echo 可以獲取配套教程,目前尚在更新中。