三分鐘小短文:一致性非鎖定讀與一致性鎖定讀
臺(tái)上三分鐘,臺(tái)下三小時(shí),兄弟們,今天咱們花三分鐘了解下數(shù)據(jù)庫中的兩種讀(select)操作:一致性非鎖定讀 和 一致性鎖定讀
一致性非鎖定讀
一致性非鎖定讀是什么?這里我先給出一個(gè)最最最簡單的解釋:一致性非鎖定讀就是讀快照!
快照即當(dāng)前行數(shù)據(jù)之前的歷史版本,每行記錄可能存在多個(gè)歷史版本,或者說每行記錄可能有不止一個(gè)快照數(shù)據(jù),一般我們將這種技術(shù)稱為 行多版本技術(shù)。而由于一個(gè)行記錄可能對應(yīng)著多個(gè)快照(歷史版本),為此不可避免地會(huì)帶來一系列的并發(fā)問題,如何解決這些并發(fā)問題,就是所謂的 多版本并發(fā)控制(MVCC),當(dāng)然,這不是本文的重點(diǎn)。
在不同事務(wù)隔離級別下,讀取的方式不同。只有在事務(wù)隔離級別 READ COMMITTED 和 REPEATABLE READ(默認(rèn))下,InnoDB 存儲(chǔ)引擎才會(huì)使用非鎖定的一致性讀。并且,即使都是使用非鎖定的一致性讀,它倆對于快照數(shù)據(jù)的定義也各不相同:
在 READ COMMITTED 事務(wù)隔離級別下,總是讀取行的最新版本;如果行被鎖定了,非一致性讀不會(huì)因此去等待行上鎖的釋放,而是去讀取該行版本的最新一個(gè)快照,如下圖所示:

img 在 REPEATABLE READ 事務(wù)隔離級別下,對于快照數(shù)據(jù),非一致性讀總是讀取事務(wù)開始時(shí)的行數(shù)據(jù)版本
這么說可能還不是很好理解,舉個(gè)例子,這個(gè)時(shí)候又得掏出我們經(jīng)典的 user 表了(滑稽),表中包含三個(gè)字段 id、username、age,已存在一行記錄:
id = 1, username = 'Jack', age = 20;
1)第一步,我們開啟一個(gè)事務(wù),執(zhí)行如下語句:
事務(wù) 1:
begin;
select * from user where id = 1;
2)可以看到,第一個(gè)事務(wù)并沒有提交,這時(shí),我們開啟第二個(gè)事務(wù)模擬并發(fā),執(zhí)行如下語句:
事務(wù) 2:
begin;
update user set id = 100 where id = 1;
3)在第二個(gè)事務(wù)中,將表中 id 為 1 的記錄修改為了 id=100,但是事務(wù)同樣沒有提交(即此時(shí) id = 1 的行記錄被事務(wù) 2 加上了行鎖)。這時(shí)如果在第一個(gè)事務(wù)中再次讀取 id 為 1 的記錄,那顯然還是 1 對吧:
事務(wù) 1:
select * from user where id = 1;

4)接著,我們再來提交下第 2 個(gè)事務(wù)中所作的修改:
事務(wù) 2:
commit;
由于當(dāng)前 id = 1 的數(shù)據(jù)被修改成了 100,也就是說,當(dāng)前 id = 100 的行記錄擁有了一個(gè) id = 1 的歷史版本。

5)這個(gè)時(shí)候,再去事務(wù) 1 中讀取 id 為 1 的記錄,在 READ COMMITTED 和 REPEATABLE 事務(wù)隔離級別下得到結(jié)果就不一樣了:
對于 READ COMMITTED 的事務(wù)隔離級別,由于事務(wù) 2 已經(jīng)提交了,也就是說 id = 1 的行記錄沒有被事務(wù) 2 鎖定,所以就會(huì)去讀取該行的最新版本,即 id = 100,So, 在 READ COMMITTED 的事務(wù)隔離級別下,此時(shí)查詢 id = 1 的結(jié)果是 Empty Set;
而在 REPEATABLE READ 事務(wù)隔離級別下,非一致性讀總是讀取事務(wù)開始時(shí)的行數(shù)據(jù)版本。也就是說,在事務(wù) 1 剛開始的時(shí)候,id = 1 的數(shù)據(jù)行是什么樣,現(xiàn)在讀到的就是什么樣的:

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

一致性鎖定讀
其實(shí)從名字上也能看出來,非一致性鎖定讀適用于對數(shù)據(jù)一致性要求不是很高的情況,比如在 READ COMMITTED 隔離級別下,即使行被鎖定了,非一致性讀也可以讀到該行版本的最新一個(gè)快照。也即,非鎖定讀機(jī)制極大地提高了數(shù)據(jù)庫的并發(fā)性。
而一致性鎖定讀適用于對數(shù)據(jù)一致性要求比較高的情況,這個(gè)時(shí)候我們需要對讀操作進(jìn)行加鎖以保證數(shù)據(jù)邏輯的一致性。
InnoDB 存儲(chǔ)引擎對讀操作支持兩種一致性鎖定讀方式,或者說對讀操作支持兩種加鎖方式:
SELECT ... FOR UPDATE,對于讀取的行記錄加一個(gè) X 排它鎖,其他事務(wù)不能對鎖定的行加任何鎖SELECT ... LOCK IN SHARE MODE,對于讀取的行記錄添加一個(gè) S 共享鎖。其它事務(wù)可以向被鎖定的行加 S 鎖,但是不允許添加 X 鎖,否則會(huì)被阻塞住
So,如何用大白話解釋一致性鎖定讀?上面這兩條特殊的 select 語句就是一致性鎖定讀!一致性鎖定讀就是給行記錄加 X 鎖或 S 鎖!
簡單不?
我是小牛肉,長風(fēng)破浪會(huì)有時(shí),小伙伴們下篇文章再見 ??

博主小碩在讀,深耕 Java,目前在維護(hù)一個(gè)教程類倉庫
CS-Wiki「Gitee 官方推薦項(xiàng)目,現(xiàn)已 1.9k+ star,倉庫地址:https://gitee.com/veal98/CS-Wiki」,公眾號上的文章也會(huì)在此同步更新,歡迎各位前來交流學(xué)習(xí)準(zhǔn)備春招秋招的小伙伴可以參考我的這個(gè)論壇項(xiàng)目 Echo「Gitee 官方推薦項(xiàng)目,現(xiàn)已 1.1k+ star,倉庫地址:https://gitee.com/veal98/Echo」。配套教程正在同步更新中,公眾號后臺(tái)回復(fù) "Echo" 即可免費(fèi)獲取。

