《Designing Data-Intensive Applications》第7章 讀書(shū)筆記(2):事務(wù)隔離級(jí)別與異常

1.說(shuō)明

如果兩個(gè)事務(wù)同時(shí)讀寫(xiě)同一份數(shù)據(jù),會(huì)引發(fā)并發(fā)競(jìng)爭(zhēng)問(wèn)題。
而理論上,隔離性會(huì)使得一切變得簡(jiǎn)單,因?yàn)榭梢宰屇阏J(rèn)為不會(huì)有并發(fā)競(jìng)爭(zhēng)出現(xiàn)
如串行化隔離,代表db保證事務(wù)執(zhí)行的結(jié)果和他們串行化執(zhí)行的結(jié)果一樣。
但是串行化的代價(jià)較高,實(shí)際上很多系統(tǒng)會(huì)用一些弱化的隔離標(biāo)準(zhǔn)
本節(jié)主要講解

幾種異常:臟讀,臟寫(xiě),Lost update,幻讀
幾種隔離標(biāo)準(zhǔn):未提交讀(書(shū)中沒(méi)有展開(kāi)),讀提交,可重復(fù)讀,串行化(下一節(jié)再講)
以及一些實(shí)現(xiàn)的講解

2.讀提交

最基本的是讀提交隔離級(jí)別,提供兩個(gè)保證

1.讀數(shù)據(jù)是只會(huì)讀到已經(jīng)提交的數(shù)據(jù)(沒(méi)有臟讀)
2.寫(xiě)數(shù)據(jù)時(shí),只會(huì)覆蓋已經(jīng)提交的寫(xiě)(沒(méi)有臟寫(xiě))

臟讀

如果一個(gè)事務(wù)的寫(xiě)還沒(méi)有提交,另外一個(gè)事務(wù)就讀到了這個(gè)未提交的寫(xiě)數(shù)據(jù),那么就叫臟讀


圖1,沒(méi)有出現(xiàn)臟讀

事務(wù)2不會(huì)讀到事務(wù)1未提交的數(shù)據(jù)x=3
解決臟讀的意義

1.避免讀到部分update,如上一節(jié)的圖2
2.如果一個(gè)事務(wù)在回滾中,它之前寫(xiě)的廢棄掉的數(shù)據(jù)不會(huì)被其他事務(wù)讀到

臟寫(xiě)

兩個(gè)事務(wù)同時(shí)寫(xiě)一份數(shù)據(jù)時(shí)會(huì)發(fā)生什么?
由于不知道明確的順序,常常假定后續(xù)的寫(xiě)會(huì)覆蓋掉前面的寫(xiě)。

那么,如果一個(gè)事務(wù)的寫(xiě)尚未提交,此時(shí)另外一個(gè)事務(wù)的寫(xiě)操作來(lái)覆蓋這個(gè)數(shù)據(jù),則稱之為臟寫(xiě)。

讀提交往往通過(guò)延遲第二個(gè)事務(wù)的寫(xiě)操作的時(shí)間,等到第一個(gè)事務(wù)進(jìn)行完了之后再進(jìn)行,這種方式來(lái)避免臟寫(xiě)。
臟寫(xiě)場(chǎng)景如下


圖2,Alice和Bob要買(mǎi)同一個(gè)東西,臟寫(xiě)導(dǎo)致了最終的買(mǎi)家是Bob,而發(fā)票卻寄給了Alice。

這里注意一下,上一節(jié)的圖1不叫臟寫(xiě),事務(wù)2的寫(xiě)在事務(wù)1提交了之后才執(zhí)行。對(duì)應(yīng)的問(wèn)題叫做Lost Updates,下節(jié)會(huì)講

實(shí)現(xiàn)讀提交

阻止臟寫(xiě)

常見(jiàn)的阻止臟寫(xiě)的方式是用行級(jí)鎖

事務(wù)要修改一個(gè)obj時(shí),現(xiàn)獲取鎖,如果獲取成功,那么一直占有鎖知道事務(wù)提交或者回滾
一次只能有一個(gè)事務(wù)獲取某個(gè)obj的鎖,此時(shí)其他事務(wù)都得等到這個(gè)鎖釋放了才能重新去獲取

阻止臟讀

當(dāng)然也可以用讀鎖,但是實(shí)際表現(xiàn)不佳。因?yàn)楹臅r(shí)長(zhǎng)的事務(wù)寫(xiě)操作會(huì)導(dǎo)致相關(guān)的只讀事務(wù)等待很長(zhǎng)時(shí)間。

很多db通過(guò)類似圖1的方式,即對(duì)于事務(wù)所有寫(xiě)的記錄,db記錄舊值和新值。
此時(shí)其他事務(wù)來(lái)進(jìn)行讀操作時(shí),返回舊值即可。
僅當(dāng)新值提交了之后才會(huì)讀到新值。

3.快照隔離和可重復(fù)讀

即使在上面讀提交的隔離級(jí)別下,還是會(huì)有并發(fā)的問(wèn)題,如下


圖3

圖的意思如下:一開(kāi)始兩個(gè)賬戶各500,有個(gè)Transfer從其中一個(gè)賬戶轉(zhuǎn)走100到另外一個(gè)賬戶。但是Alice讀第一個(gè)賬戶為500,讀第二個(gè)賬戶為400.出現(xiàn)了數(shù)據(jù)不一致的情況。

這個(gè)現(xiàn)象稱為不可重復(fù)讀
如果Alice重新去讀取兩個(gè)account,就會(huì)發(fā)現(xiàn)一個(gè)600(之前是500)一個(gè)400,達(dá)到數(shù)據(jù)一致(一共1000)
在讀提交的隔離級(jí)別下,不可重復(fù)讀是允許的
上面的例子中,Alice只要重新check一遍就好。但是有些場(chǎng)景不允許這種臨時(shí)的不一致,如
1.備份
2.數(shù)據(jù)分析以及一致性檢測(cè)

常常通過(guò)快照隔離完成,快照隔離就是

每個(gè)事務(wù)從一個(gè)一致性快照中讀取數(shù)據(jù),也就是事務(wù)開(kāi)始時(shí),只會(huì)看到所有已經(jīng)提交的數(shù)據(jù)。即使有些數(shù)據(jù)會(huì)被其他事務(wù)修改,當(dāng)前書(shū)屋也只會(huì)看到特定時(shí)間點(diǎn)的舊數(shù)據(jù)

快照就是耗時(shí)長(zhǎng)的只讀統(tǒng)計(jì)類事務(wù)如備份以及數(shù)據(jù)分析的福音。

實(shí)現(xiàn)快照隔離

原則:讀事務(wù)與寫(xiě)事務(wù)互不影響。
這使得db能夠在一致性快照基礎(chǔ)上處理讀事務(wù)的同時(shí),處理寫(xiě)請(qǐng)求。而不需要讀寫(xiě)兩個(gè)事務(wù)之間有鎖。

為了實(shí)現(xiàn)快照隔離,db需要類似圖1中的機(jī)制,保留一個(gè)obj的新舊版本。來(lái)保證不同的事務(wù)能夠看到數(shù)據(jù)不同的狀態(tài)。這個(gè)技術(shù)稱為MVCC(multi version concurrency control)

和讀提交的區(qū)別:
1.幾個(gè)版本
讀提交中,obj只用兩個(gè)版本即可,已提交版本和覆蓋但未提交的版本(因?yàn)橛墟i,不用多個(gè)版本)
快照隔離中,用MVCC,是multi version的(因?yàn)闆](méi)有鎖,可能多個(gè)事務(wù)同時(shí)寫(xiě))

2.幾個(gè)快照
讀提交中,一個(gè)事務(wù)每次讀都是讀不同的快照(參照?qǐng)D3理解)
快照隔離中,一個(gè)事務(wù)中所有讀都是讀同一個(gè)快照

MVCC實(shí)現(xiàn)中,每個(gè)事務(wù)都要有一個(gè)唯一的自增id(txid),實(shí)現(xiàn)如下圖


MVCC實(shí)現(xiàn)快照隔離

注意

上圖每個(gè)update行為轉(zhuǎn)化成了一個(gè)delete以及一個(gè)create.
每條記錄有created_by以及deleted_by字段.
當(dāng)沒(méi)有事務(wù)訪問(wèn)刪除的記錄時(shí),后臺(tái)會(huì)有g(shù)c進(jìn)程回收deleted_by不為空的記錄

MVCC中每個(gè)obj可能有多個(gè)版本,那么一個(gè)事務(wù)進(jìn)來(lái)讀取數(shù)據(jù)的時(shí)候,到底是讀取哪個(gè)版本呢?
引出下面這個(gè)話題

一致性快照的可見(jiàn)性原則

事務(wù)id(txid)來(lái)決定哪些obj可見(jiàn)

1.事務(wù)開(kāi)始前列出當(dāng)前進(jìn)行的(未提交以及丟棄)的事務(wù)id,這些id的提交是不可見(jiàn)的
2.丟棄的事務(wù),忽略
3.后續(xù)的事務(wù),忽略
4.所有其他的寫(xiě)都會(huì)被讀到(就相當(dāng)于之前已經(jīng)有的記錄)

換一種方式理解,一個(gè)object對(duì)于一個(gè)事務(wù)可見(jiàn),當(dāng):

1.讀事務(wù)開(kāi)始時(shí),創(chuàng)建該obj的事務(wù)已經(jīng)提交
2.該obj沒(méi)有被刪除,或者說(shuō)刪除的事務(wù)在讀事務(wù)開(kāi)始執(zhí)行時(shí)還未提交

索引以及快照隔離

如何讓索引也能在MVCC中工作呢?
一種方法是索引指向數(shù)據(jù)的所有版本,由可見(jiàn)性去判斷正確的版本。
當(dāng)deleted_by不為空的記錄被gc進(jìn)程處理后,index也會(huì)相應(yīng)的更新。

其他的一些方式是B樹(shù)上用一些變種(append-only/copy-on-write等),這里不深入展開(kāi)

快照隔離的命名困惑

快照隔離在不同的db中叫法不一樣,這是因?yàn)镾QL標(biāo)準(zhǔn)里面,并沒(méi)有“快照隔離”這個(gè)概念

4.Lost update

圖1的情況中,兩個(gè)事務(wù)同時(shí)寫(xiě)一個(gè)數(shù)據(jù)時(shí),出現(xiàn)了類似丟失更新的問(wèn)題
這個(gè)被稱為Lost update,參照refer中給的一點(diǎn)定義,如下

當(dāng)兩個(gè)或多個(gè)事務(wù)選擇同一行,然后基于最初選定的值更新該行時(shí),由于每個(gè)事務(wù)都不知道其他事務(wù)的存在,就會(huì)發(fā)生丟失更新問(wèn)題

一般解決方式如下,不展開(kāi):

原子寫(xiě)操作
顯式用鎖(for update語(yǔ)句)
CAS操作

不過(guò)在分布式系統(tǒng)中,并發(fā)的寫(xiě)沖突可能往往靠應(yīng)用程序自己解決,這個(gè)請(qǐng)看前面的相關(guān)章節(jié).

5.幻讀

除了上面的競(jìng)爭(zhēng)外,還有其他并發(fā)寫(xiě)可能會(huì)出現(xiàn)的競(jìng)爭(zhēng),如下
醫(yī)院在任何時(shí)候必須至少有一位醫(yī)生在值班。醫(yī)生可以調(diào)整他們的輪班,前提是至少有一個(gè)同事在醫(yī)院值班。Alice和Bob是兩位今天值班的醫(yī)生。兩人都想調(diào)整輪班,不幸的是,他們碰巧點(diǎn)擊按鈕大約在同一時(shí)間取消輪班。接下來(lái)發(fā)生的情況如圖所示:


幻讀

這個(gè)問(wèn)題既不是臟寫(xiě)又不是lost updates,因?yàn)閮蓚€(gè)事物在update兩個(gè)不同的obj(Alice和Bob各自的值班表)。這個(gè)問(wèn)題不明顯,但毫無(wú)疑問(wèn)是競(jìng)爭(zhēng),因?yàn)槿绻谢瘓?zhí)行就不會(huì)有這個(gè)問(wèn)題了。

參照之前解決lost updates的方法,解決幻讀則需要

1.單obj的原子操作不管用了,需要多obj的原子操作
2.需要串行化的隔離級(jí)別(后續(xù)再講)
3.有些db可以配置約束,自行選擇觸發(fā)器或者視圖相關(guān)
4.select for update鎖住多行

幻讀的發(fā)生,都會(huì)有特定的形式,這里不用書(shū)中給的定義,用refer中的博客提到的感覺(jué)說(shuō)的更好

幻讀發(fā)生在正在執(zhí)行的事務(wù) T1 有斷言的讀 (select where) 時(shí),另外一個(gè)事務(wù) T2 執(zhí)行了和斷言集合有交集的插入操作。

6.總結(jié)

這一節(jié)講了幾種異常

臟讀,臟寫(xiě)
Lost updates
幻讀

對(duì)應(yīng)幾種解決的隔離級(jí)別

未提交讀
提交讀
可重復(fù)讀

這一節(jié)沒(méi)有講串行化,篇幅原因,下一節(jié)再講
幾個(gè)隔離級(jí)別的翻譯是參照網(wǎng)絡(luò)上的定義

參照refer中 https://zhuanlan.zhihu.com/p/29166694
對(duì)應(yīng)關(guān)系如下

各具體數(shù)據(jù)庫(kù)并不一定完全實(shí)現(xiàn)了上述 4 個(gè)隔離級(jí)別

7.思考

讀提交和可重復(fù)讀的區(qū)別
實(shí)現(xiàn)快照隔離中 講解過(guò)

MVCC多個(gè)版本中,一個(gè)事務(wù)讀取時(shí)該選擇哪個(gè)版本

參照上述 可見(jiàn)性原則

Lost Update
按照refer中,這個(gè)似乎用鎖避免并發(fā)寫(xiě)就可以(http://blog.csdn.net/bluishglc/article/details/5626009),也就是未提交讀的隔離級(jí)別就能解決的。
因此感覺(jué)文章順序有點(diǎn)問(wèn)題,這里應(yīng)該放在最前面,而且注意 Lost update不是隔離級(jí)別,只是一個(gè)并發(fā)考慮的問(wèn)題

Lost update與臟寫(xiě)的區(qū)別
能保證不出現(xiàn)臟寫(xiě)的隔離級(jí)別,不一定保證不會(huì)出現(xiàn)Lost update
Lost update的情形中,兩個(gè)事務(wù)都沒(méi)有看到對(duì)方未commit的數(shù)據(jù)(臟寫(xiě)是看得到的)

個(gè)人感受
不同的情形可能都需要深挖,書(shū)籍也有沒(méi)有完全的深入講解,點(diǎn)到即止。
自己知道這些東西就好

8.名詞

隔離標(biāo)準(zhǔn)
讀提交
臟讀,臟寫(xiě)
(不)可重復(fù)讀
快照隔離
MVCC(multi version concurrency control)
lost updates
幻讀

9.refer

http://www.itdecent.cn/p/a84e4f41a2aa

lost update相關(guān)
http://blog.csdn.net/bluishglc/article/details/5626009
https://zhuanlan.zhihu.com/p/29166694

幻讀
這篇博客寫(xiě)的太厲害了 https://ggaaooppeenngg.github.io/zh-CN/2017/04/16/SQL%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB/

?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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