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ù),那么就叫臟讀

事務(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)景如下

這里注意一下,上一節(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)題,如下

圖的意思如下:一開(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)如下圖

注意
上圖每個(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)系如下

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/