一、事務(wù)相關(guān)知識(shí)的回顧

1、聊聊事務(wù)的概念與特性

記憶中第一次準(zhǔn)確了解到“事務(wù)”這個(gè)詞的時(shí)候,是在大學(xué)數(shù)據(jù)庫(kù)的課堂上吧。
數(shù)據(jù)庫(kù)作為數(shù)據(jù)存儲(chǔ)持久化的地方,如何保證一次操作的整體性,使得這次操作具有現(xiàn)實(shí)意義,應(yīng)該是數(shù)據(jù)庫(kù)必須要具備的能力。這么一看,在數(shù)據(jù)庫(kù)的課堂上接觸到“事務(wù)”這個(gè)詞,是理所應(yīng)當(dāng)?shù)氖虑槁铩?/p>

讓我給“事務(wù)”下一個(gè)定義的話(huà):
一個(gè)包含多個(gè)動(dòng)作的事情,且這個(gè)事情只有執(zhí)行完成或沒(méi)有執(zhí)行的狀態(tài)。當(dāng)其中的一系列動(dòng)作沒(méi)有按照步驟完整地成功執(zhí)行,就意味著事情執(zhí)行失敗,并應(yīng)當(dāng)恢復(fù)到事情開(kāi)始執(zhí)行之前的狀態(tài)。

這么一說(shuō)不難看出“事務(wù)”一個(gè)非常重要的特征:原子性(atomicity)。為啥叫原子性,大概是人們認(rèn)為原子是不可切割的最小個(gè)體吧(提問(wèn):夸克呢?嗯,就你話(huà)多

再看看上面下的定義,還涉及到關(guān)于狀態(tài)變更的說(shuō)明。對(duì)于事情的執(zhí)行,它對(duì)于周?chē)臓顟B(tài)影響,應(yīng)該只有執(zhí)行前和執(zhí)行后。簡(jiǎn)單地說(shuō),事情只會(huì)使得周?chē)鷱囊粋€(gè)狀態(tài)轉(zhuǎn)換到另外一個(gè)狀態(tài)。當(dāng)然這是建立在原子性這一特性上面的。如果事情不存在原子性,即其中操作能夠被部分執(zhí)行,那么就會(huì)出現(xiàn)其他的中間狀態(tài)。對(duì)于這個(gè)特性被稱(chēng)作:一致性(consistency)。關(guān)于這個(gè)一致性,也是否意味著只要這個(gè)事情進(jìn)行執(zhí)行了,就肯定會(huì)發(fā)生從狀態(tài)A轉(zhuǎn)移到狀態(tài)B的情況,這個(gè)是個(gè)有趣的說(shuō)法,我覺(jué)得這個(gè)問(wèn)題大家也能去想一下。

上面的這兩個(gè)特性,我更愿意把它們劃分為是“事務(wù)”的內(nèi)部特性,即這是能夠從一個(gè)單獨(dú)的事務(wù)中就能體現(xiàn)出來(lái)的。所以我們不妨引出一個(gè)問(wèn)題,如果有多個(gè)不同的事務(wù),那么又會(huì)有怎樣的特性呢。事務(wù)1能夠從狀態(tài)A轉(zhuǎn)換到狀態(tài)B,事務(wù)2能夠從狀態(tài)C轉(zhuǎn)換到狀態(tài)D。在狀態(tài)A、B和狀態(tài)C、D都是可以重疊不沖突的情況下,這兩個(gè)事務(wù)的同時(shí)執(zhí)行,是不是能夠把周?chē)膹腁、C態(tài)轉(zhuǎn)換到B、D態(tài)呢,這個(gè)我理解是沒(méi)有什么爭(zhēng)議的。我們不妨來(lái)?yè)Q一個(gè)情況,如果狀態(tài)A可以轉(zhuǎn)換成為狀態(tài)B或者狀態(tài)C,那么事務(wù)1是完成從A到B,事務(wù)2是完成從A到C的。在事務(wù)1、事務(wù)2同時(shí)執(zhí)行的情況下,我們就會(huì)發(fā)現(xiàn)問(wèn)題了,那么最后是變成狀態(tài)B還是狀態(tài)C?第一種情況能夠完成的本質(zhì)原因,是因?yàn)閮蓚€(gè)事務(wù)并沒(méi)有對(duì)狀態(tài)這個(gè)資源進(jìn)行搶占,沒(méi)有搶占的原因是,他們獨(dú)立且并不沖突。但是在事務(wù)們對(duì)同一個(gè)狀態(tài)轉(zhuǎn)換存在沖突的時(shí)候呢?所以為了保證事務(wù)的順利執(zhí)行,需要引入一個(gè)用于執(zhí)行的特性:隔離性(isolation)隔離性為我們提供一個(gè)保證:事務(wù)的執(zhí)行應(yīng)當(dāng)是互不干擾的。對(duì)于互不干擾,最好的解決方式就是串行執(zhí)行了吧。但是不可能都會(huì)串行執(zhí)行啊,效率太低了啊,對(duì)吧。所以我們不妨關(guān)注一下后續(xù)的相關(guān)內(nèi)容,看看對(duì)于隔離性,這些解決方案都是怎么解決的。

隔離性告訴了我們事務(wù)執(zhí)行的過(guò)程的特性了,那么執(zhí)行完了之后呢?那不就是從狀態(tài)A變成了狀態(tài)B嘛。我理解這個(gè)狀態(tài)的變換是遵從“牛頓第一定律”的,即在沒(méi)有外力的情況,它將一直保持靜止或勻速直線(xiàn)運(yùn)動(dòng)(好像有點(diǎn)串場(chǎng)了)。所以這個(gè)狀態(tài)的變換是會(huì)一直保存下去的,我們稱(chēng)之為:持久性(durability)。

到此為止,事務(wù)的四個(gè)特性就引出來(lái)了,但是看上去,我認(rèn)為這個(gè)更像是四個(gè)要求,就是這個(gè)四個(gè)特性組合在一起就誕生了“事務(wù)”一個(gè)概念,而不是“事務(wù)”本身就具備的。看上去這個(gè)思考的過(guò)程雖然有點(diǎn)長(zhǎng),但是是順其自然的,能夠很清晰地說(shuō)明事務(wù)本身本必備的特性。主要是我一直覺(jué)得ACID這個(gè)簡(jiǎn)稱(chēng)記憶起來(lái)也太死記硬背,不利于知識(shí)的理解,對(duì)吧。


2、“壞情況”盤(pán)點(diǎn)

回應(yīng)上文,對(duì)于“隔離性”的保障是事務(wù)執(zhí)行過(guò)程中必須要關(guān)注的,否則就喪失了“事務(wù)”給我們來(lái)的的意義了。
在提到說(shuō)到這些保障措施的時(shí)候,我們其實(shí)更應(yīng)該先來(lái)看看,可能出現(xiàn)的各種錯(cuò)誤情形。上面引出隔離性的時(shí)候,比較模糊地用狀態(tài)A、B等來(lái)替代,未免也太不專(zhuān)業(yè)了。所以下面的說(shuō)明,會(huì)用更完整的操作和狀態(tài)樣例,來(lái)說(shuō)明“壞情況”。先清楚知道“壞情況”及其來(lái)源,才能夠引導(dǎo)我們思考,為什么要提出這樣的措施,也就是隔離方案來(lái)避免這樣的情況。

“壞情況”一覽:

(1)臟讀(dirty read)

抄來(lái)的樣例情景:

假設(shè)我們現(xiàn)在有這樣一張表(T),里面記錄了很多牛人的名字
第一天,事務(wù)A訪(fǎng)問(wèn)了數(shù)據(jù)庫(kù),它干了一件事情,往數(shù)據(jù)庫(kù)里加上了新來(lái)的牛人的名字,但是沒(méi)有提交事務(wù)。 
insert into T values (4, '牛D');
這時(shí),來(lái)了另一個(gè)事務(wù)B,他要查詢(xún)所有牛人的名字。
select Name from T;
這時(shí),如果沒(méi)有事務(wù)之間沒(méi)有有效隔離,那么事務(wù)B返回的結(jié)果中就會(huì)出現(xiàn)“牛D”的名字。這就是“臟讀(dirty read)”。

場(chǎng)景特點(diǎn):對(duì)一條數(shù)據(jù)僅讀了一次,就讀到了不正確的臟數(shù)據(jù)。

(2)不可重復(fù)讀(unrepeatable read)

前文再續(xù)書(shū)接上一回的情景:

第二天,事務(wù)A訪(fǎng)問(wèn)了數(shù)據(jù)庫(kù),他要查看ID是1的牛人的名字,于是執(zhí)行了
select Name from T where ID = 1;
這時(shí),事務(wù)B來(lái)了,因?yàn)镮D是1的牛人改名字了,所以要更新一下,然后提交了事務(wù)。
update T set Name = '不牛' where ID = 1;
接著,事務(wù)A還想再看看ID是1的牛人的名字,于是又執(zhí)行了
select Name from T where ID = 1;
結(jié)果,兩次讀出來(lái)的ID是1的牛人名字竟然不相同,這就是不可重復(fù)讀(unrepeatable read)。

場(chǎng)景特點(diǎn):對(duì)于同一條已有的數(shù)據(jù),前后讀了兩次,但是讀了兩次的數(shù)據(jù)不一樣。

(3)幻讀(phantom problem)

再一次前文再續(xù)書(shū)接上一回的情景:

第三天,事務(wù)A訪(fǎng)問(wèn)了數(shù)據(jù)庫(kù),他想要看看數(shù)據(jù)庫(kù)的牛人都有哪些,于是執(zhí)行了
select * from T;
這時(shí)候,事務(wù)B來(lái)了,往數(shù)據(jù)庫(kù)加入了一個(gè)新的牛人。
insert into T values(4, '牛D');
這時(shí)候,事務(wù)A忘了剛才的牛人都有哪些了,于是又執(zhí)行了。
select * from T;
結(jié)果,第一次有三個(gè)牛人,第二次有四個(gè)牛人。
相信這個(gè)時(shí)候事務(wù)A就蒙了,剛才發(fā)生了什么?這種情況就叫“幻讀(phantom problem)”。

場(chǎng)景特點(diǎn):針對(duì)某一張表的數(shù)據(jù)進(jìn)行的列表查詢(xún),兩次數(shù)量不一致。

(4) 更新丟失

對(duì)不起,隔壁抄來(lái)的沒(méi)有這個(gè)場(chǎng)景。是的,所以也沒(méi)有英文名稱(chēng)
這種情況其實(shí)非常容易理解,就是兩個(gè)事務(wù)同時(shí)去更新同一條數(shù)據(jù),比如事務(wù)A需要對(duì)該數(shù)據(jù)進(jìn)行“+100”的操作,而事務(wù)B需要對(duì)數(shù)據(jù)進(jìn)行“+200”的操作,那么在沒(méi)有對(duì)該數(shù)據(jù)加上“排他寫(xiě)鎖”,即同時(shí)只允許一個(gè)事務(wù)對(duì)這個(gè)數(shù)據(jù)的寫(xiě)操作權(quán)限的情況下,事務(wù)A、B執(zhí)行完成后可能并沒(méi)有出現(xiàn)預(yù)期的數(shù)據(jù)“+300”的操作,也就是出現(xiàn)了更新丟失的情況,可能數(shù)據(jù)僅會(huì)出現(xiàn)“+100”或者“+200”的任一情況。

總結(jié)一下

再來(lái)三種“壞情況”進(jìn)行一下回顧:

情況 原因?qū)е?/th>
臟讀(dirty read) 單次事務(wù)在未執(zhí)行提交完成時(shí),有其他人進(jìn)行讀取并獲取了未執(zhí)行完成的數(shù)據(jù)
不可重復(fù)讀(unrepeatable read) 一個(gè)事務(wù)對(duì)同一條數(shù)據(jù)進(jìn)行兩次讀取時(shí),有其他事務(wù)在期間進(jìn)行了數(shù)據(jù)更新,導(dǎo)致前后不一致
幻讀(phantom problem) 一個(gè)事務(wù)對(duì)一張表數(shù)據(jù)進(jìn)行兩次讀取時(shí),有其他事務(wù)對(duì)表數(shù)據(jù)進(jìn)行了數(shù)據(jù)的插入或者刪除,導(dǎo)致前后數(shù)量不一致
更新丟失 多個(gè)事務(wù)同時(shí)對(duì)同一個(gè)數(shù)據(jù)進(jìn)行更新,導(dǎo)致只有部分執(zhí)行結(jié)果被保留,其余執(zhí)行結(jié)果被覆蓋丟失

由此,我們引出幾種等級(jí)的隔離策略,來(lái)試圖解決上面出現(xiàn)的幾種“壞情況”。


3、解決方案:事務(wù)隔離

對(duì)于上述的四種“壞情況”,不可能只有兩種隔離策略來(lái)說(shuō)要么四個(gè)種情況我都能完美避免,或者四種情況我都不聞不問(wèn),應(yīng)當(dāng)存在一些中間的分級(jí)策略,來(lái)解決部分的情況,來(lái)達(dá)到最低限度的要求,畢竟我們還是要兼顧性能的。

(1)讀未提交(Read uncommitted)

最低級(jí)別的隔離策略。
如果一個(gè)事務(wù)已經(jīng)開(kāi)始寫(xiě)數(shù)據(jù),則另外一個(gè)事務(wù)不允許同時(shí)進(jìn)行寫(xiě)操作,但允許其他事務(wù)讀此行數(shù)據(jù),該隔離級(jí)別可以通過(guò)“排他寫(xiě)鎖”,但是不排斥讀線(xiàn)程實(shí)現(xiàn)。
在該隔離等級(jí)下,解決了更新丟失的情況,因?yàn)橥粫r(shí)間僅會(huì)有一個(gè)事務(wù)擁有對(duì)該數(shù)據(jù)的寫(xiě)權(quán)限。其余事務(wù)需要等待擁有寫(xiě)權(quán)限的事務(wù)完成后再重新發(fā)起占據(jù)之后才進(jìn)行數(shù)據(jù)的更新。
從名字上來(lái)看我們也知道,在這種隔離粗略下,能夠讀到未提交事務(wù)的數(shù)據(jù),也就是會(huì)出現(xiàn)臟讀的情況。在該隔離等級(jí)下,僅解決了更新丟失的情況。

(2)讀提交(Read committed)

如果是一個(gè)讀事務(wù)(線(xiàn)程),則允許其他事務(wù)讀寫(xiě),如果是寫(xiě)事務(wù)將會(huì)禁止其他事務(wù)訪(fǎng)問(wèn)該行數(shù)據(jù)。
跟上一個(gè)等級(jí)比較,很明顯的差異就是,在寫(xiě)事務(wù)執(zhí)行的時(shí)候,是禁止其他事務(wù)訪(fǎng)問(wèn)該數(shù)據(jù),即連讀取也是不允許的。在該隔離等級(jí)情況下,數(shù)據(jù)的更新將在完成之后才會(huì)被讀取到,不會(huì)出現(xiàn)未執(zhí)行完成的數(shù)據(jù)被讀取,所以能夠避免臟讀的情況。但是由于讀事務(wù)并不應(yīng)該寫(xiě)事務(wù)對(duì)同一數(shù)據(jù)的執(zhí)行,即在讀的過(guò)程中,忽然有寫(xiě)事務(wù)執(zhí)行,在寫(xiě)事務(wù)執(zhí)行完成后開(kāi)始的讀事務(wù)再次讀取同一數(shù)據(jù)時(shí),仍然會(huì)出現(xiàn)不一致的情況。所以不可重復(fù)讀在該隔離等級(jí)還是會(huì)出現(xiàn)的。

(3)可重復(fù)讀(Repeated Read)

可重復(fù)讀取是指在一個(gè)事務(wù)內(nèi),多次讀同一個(gè)數(shù)據(jù),在這個(gè)事務(wù)還沒(méi)結(jié)束時(shí),其他事務(wù)不能訪(fǎng)問(wèn)該數(shù)據(jù)(包括了讀寫(xiě))。
和“讀提交”相比,“可重復(fù)讀”采取了更進(jìn)一步的要求,在該事務(wù)還沒(méi)有結(jié)束時(shí),即包括讀事務(wù)在內(nèi),其他事務(wù)都不允許對(duì)該數(shù)據(jù)進(jìn)行讀寫(xiě),這樣子就能夠有效避免同一事務(wù)中兩次讀取相同數(shù)據(jù)的差異,即不會(huì)發(fā)生不可重復(fù)讀的情況了。

(4)串行化(Serializable)

提供嚴(yán)格的事務(wù)隔離,它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個(gè)接著一個(gè)地執(zhí)行,但不能并發(fā)執(zhí)行。
呼應(yīng)上文中,對(duì)于隔離性最好的解決方案就是,事務(wù)的順序執(zhí)行,這樣子就不會(huì)發(fā)生對(duì)同一數(shù)據(jù),甚至表格內(nèi)容的搶占了。能夠避免上文中提到的所有“壞情況的發(fā)生”。

總結(jié)一下

隔離級(jí)別 解決情況 備注
讀未提交(Read uncommitted) 更新丟失 最低等級(jí)的隔離策略,不建議在生產(chǎn)環(huán)境使用
讀提交(Read committed) 更新丟失、臟讀 最常用的隔離等級(jí),是SQL Server和Oracle的默認(rèn)隔離級(jí)別
可重復(fù)讀(Repeated Read) 更新丟失、臟讀、不可重復(fù)讀 MySql的默認(rèn)隔離級(jí)別
串行化(Serializable) 全部解決 效率最為底下

引用參考:
https://baijiahao.baidu.com/s?id=1611918898724887602&wfr=spider&for=pc

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容