1.說明
如果兩個事務(wù)同時讀寫同一份數(shù)據(jù)茅逮,會引發(fā)并發(fā)競爭問題言缤。
而理論上,隔離性會使得一切變得簡單导而,因為可以讓你認(rèn)為不會有并發(fā)競爭出現(xiàn)
如串行化隔離忱叭,代表db保證事務(wù)執(zhí)行的結(jié)果和他們串行化執(zhí)行的結(jié)果一樣。
但是串行化的代價較高今艺,實際上很多系統(tǒng)會用一些弱化的隔離標(biāo)準(zhǔn)
本節(jié)主要講解
幾種異常:臟讀韵丑,臟寫,Lost update虚缎,幻讀
幾種隔離標(biāo)準(zhǔn):未提交讀(書中沒有展開)撵彻,讀提交,可重復(fù)讀实牡,串行化(下一節(jié)再講)
以及一些實現(xiàn)的講解
2.讀提交
最基本的是讀提交隔離級別陌僵,提供兩個保證
1.讀數(shù)據(jù)是只會讀到已經(jīng)提交的數(shù)據(jù)(沒有臟讀)
2.寫數(shù)據(jù)時,只會覆蓋已經(jīng)提交的寫(沒有臟寫)
臟讀
如果一個事務(wù)的寫還沒有提交创坞,另外一個事務(wù)就讀到了這個未提交的寫數(shù)據(jù)碗短,那么就叫臟讀
事務(wù)2不會讀到事務(wù)1未提交的數(shù)據(jù)x=3
解決臟讀的意義
1.避免讀到部分update题涨,如上一節(jié)的圖2
2.如果一個事務(wù)在回滾中偎谁,它之前寫的廢棄掉的數(shù)據(jù)不會被其他事務(wù)讀到
臟寫
兩個事務(wù)同時寫一份數(shù)據(jù)時會發(fā)生什么总滩?
由于不知道明確的順序,常常假定后續(xù)的寫會覆蓋掉前面的寫巡雨。
那么闰渔,如果一個事務(wù)的寫尚未提交,此時另外一個事務(wù)的寫操作來覆蓋這個數(shù)據(jù)铐望,則稱之為臟寫澜建。
讀提交往往通過延遲第二個事務(wù)的寫操作的時間,等到第一個事務(wù)進(jìn)行完了之后再進(jìn)行蝌以,這種方式來避免臟寫炕舵。
臟寫場景如下
這里注意一下徊件,上一節(jié)的圖1不叫臟寫奸攻,事務(wù)2的寫在事務(wù)1提交了之后才執(zhí)行。對應(yīng)的問題叫做Lost Updates,下節(jié)會講
實現(xiàn)讀提交
阻止臟寫
常見的阻止臟寫的方式是用行級鎖
事務(wù)要修改一個obj時虱痕,現(xiàn)獲取鎖睹耐,如果獲取成功,那么一直占有鎖知道事務(wù)提交或者回滾
一次只能有一個事務(wù)獲取某個obj的鎖部翘,此時其他事務(wù)都得等到這個鎖釋放了才能重新去獲取
阻止臟讀
當(dāng)然也可以用讀鎖硝训,但是實際表現(xiàn)不佳。因為耗時長的事務(wù)寫操作會導(dǎo)致相關(guān)的只讀事務(wù)等待很長時間新思。
很多db通過類似圖1的方式窖梁,即對于事務(wù)所有寫的記錄,db記錄舊值和新值夹囚。
此時其他事務(wù)來進(jìn)行讀操作時纵刘,返回舊值即可。
僅當(dāng)新值提交了之后才會讀到新值荸哟。
3.快照隔離和可重復(fù)讀
即使在上面讀提交的隔離級別下假哎,還是會有并發(fā)的問題,如下
圖的意思如下:一開始兩個賬戶各500鞍历,有個Transfer從其中一個賬戶轉(zhuǎn)走100到另外一個賬戶舵抹。但是Alice讀第一個賬戶為500,讀第二個賬戶為400.出現(xiàn)了數(shù)據(jù)不一致的情況堰燎。
這個現(xiàn)象稱為不可重復(fù)讀
如果Alice重新去讀取兩個account掏父,就會發(fā)現(xiàn)一個600(之前是500)一個400笋轨,達(dá)到數(shù)據(jù)一致(一共1000)
在讀提交的隔離級別下秆剪,不可重復(fù)讀是允許的
上面的例子中赊淑,Alice只要重新check一遍就好。但是有些場景不允許這種臨時的不一致,如
1.備份
2.數(shù)據(jù)分析以及一致性檢測
常常通過快照隔離完成,快照隔離就是
每個事務(wù)從一個一致性快照中讀取數(shù)據(jù)仅讽,也就是事務(wù)開始時陶缺,只會看到所有已經(jīng)提交的數(shù)據(jù)。即使有些數(shù)據(jù)會被其他事務(wù)修改洁灵,當(dāng)前書屋也只會看到特定時間點的舊數(shù)據(jù)
快照就是耗時長的只讀統(tǒng)計類事務(wù)如備份以及數(shù)據(jù)分析的福音饱岸。
實現(xiàn)快照隔離
原則:讀事務(wù)與寫事務(wù)互不影響。
這使得db能夠在一致性快照基礎(chǔ)上處理讀事務(wù)的同時徽千,處理寫請求苫费。而不需要讀寫兩個事務(wù)之間有鎖。
為了實現(xiàn)快照隔離双抽,db需要類似圖1中的機(jī)制百框,保留一個obj的新舊版本。來保證不同的事務(wù)能夠看到數(shù)據(jù)不同的狀態(tài)牍汹。這個技術(shù)稱為MVCC(multi version concurrency control)
和讀提交的區(qū)別:
1.幾個版本
讀提交中铐维,obj只用兩個版本即可,已提交版本和覆蓋但未提交的版本(因為有鎖慎菲,不用多個版本)
快照隔離中嫁蛇,用MVCC,是multi version的(因為沒有鎖露该,可能多個事務(wù)同時寫)2.幾個快照
讀提交中睬棚,一個事務(wù)每次讀都是讀不同的快照(參照圖3理解)
快照隔離中,一個事務(wù)中所有讀都是讀同一個快照
MVCC實現(xiàn)中解幼,每個事務(wù)都要有一個唯一的自增id(txid)闸拿,實現(xiàn)如下圖
注意
上圖每個update行為轉(zhuǎn)化成了一個delete以及一個create.
每條記錄有created_by以及deleted_by字段.
當(dāng)沒有事務(wù)訪問刪除的記錄時,后臺會有g(shù)c進(jìn)程回收deleted_by不為空的記錄
MVCC中每個obj可能有多個版本书幕,那么一個事務(wù)進(jìn)來讀取數(shù)據(jù)的時候新荤,到底是讀取哪個版本呢?
引出下面這個話題
一致性快照的可見性原則
事務(wù)id(txid)來決定哪些obj可見
1.事務(wù)開始前列出當(dāng)前進(jìn)行的(未提交以及丟棄)的事務(wù)id台汇,這些id的提交是不可見的
2.丟棄的事務(wù)苛骨,忽略
3.后續(xù)的事務(wù),忽略
4.所有其他的寫都會被讀到(就相當(dāng)于之前已經(jīng)有的記錄)
換一種方式理解苟呐,一個object對于一個事務(wù)可見痒芝,當(dāng):
1.讀事務(wù)開始時,創(chuàng)建該obj的事務(wù)已經(jīng)提交
2.該obj沒有被刪除牵素,或者說刪除的事務(wù)在讀事務(wù)開始執(zhí)行時還未提交
索引以及快照隔離
如何讓索引也能在MVCC中工作呢严衬?
一種方法是索引指向數(shù)據(jù)的所有版本,由可見性去判斷正確的版本笆呆。
當(dāng)deleted_by不為空的記錄被gc進(jìn)程處理后请琳,index也會相應(yīng)的更新粱挡。
其他的一些方式是B樹上用一些變種(append-only/copy-on-write等),這里不深入展開
快照隔離的命名困惑
快照隔離在不同的db中叫法不一樣俄精,這是因為SQL標(biāo)準(zhǔn)里面询筏,并沒有“快照隔離”這個概念
4.Lost update
圖1的情況中,兩個事務(wù)同時寫一個數(shù)據(jù)時竖慧,出現(xiàn)了類似丟失更新的問題
這個被稱為Lost update嫌套,參照refer中給的一點定義,如下
當(dāng)兩個或多個事務(wù)選擇同一行圾旨,然后基于最初選定的值更新該行時踱讨,由于每個事務(wù)都不知道其他事務(wù)的存在,就會發(fā)生丟失更新問題
一般解決方式如下砍的,不展開:
原子寫操作
顯式用鎖(for update語句)
CAS操作
不過在分布式系統(tǒng)中勇蝙,并發(fā)的寫沖突可能往往靠應(yīng)用程序自己解決,這個請看前面的相關(guān)章節(jié).
5.幻讀
除了上面的競爭外挨约,還有其他并發(fā)寫可能會出現(xiàn)的競爭味混,如下
醫(yī)院在任何時候必須至少有一位醫(yī)生在值班。醫(yī)生可以調(diào)整他們的輪班诫惭,前提是至少有一個同事在醫(yī)院值班翁锡。Alice和Bob是兩位今天值班的醫(yī)生。兩人都想調(diào)整輪班夕土,不幸的是馆衔,他們碰巧點擊按鈕大約在同一時間取消輪班。接下來發(fā)生的情況如圖所示:
這個問題既不是臟寫又不是lost updates怨绣,因為兩個事物在update兩個不同的obj(Alice和Bob各自的值班表)角溃。這個問題不明顯,但毫無疑問是競爭篮撑,因為如果串行化執(zhí)行就不會有這個問題了减细。
參照之前解決lost updates的方法,解決幻讀則需要
1.單obj的原子操作不管用了赢笨,需要多obj的原子操作
2.需要串行化的隔離級別(后續(xù)再講)
3.有些db可以配置約束未蝌,自行選擇觸發(fā)器或者視圖相關(guān)
4.select for update鎖住多行
幻讀的發(fā)生,都會有特定的形式茧妒,這里不用書中給的定義萧吠,用refer中的博客提到的感覺說的更好
幻讀發(fā)生在正在執(zhí)行的事務(wù) T1 有斷言的讀 (select where) 時,另外一個事務(wù) T2 執(zhí)行了和斷言集合有交集的插入操作桐筏。
6.總結(jié)
這一節(jié)講了幾種異常
臟讀纸型,臟寫
Lost updates
幻讀
對應(yīng)幾種解決的隔離級別
未提交讀
提交讀
可重復(fù)讀
這一節(jié)沒有講串行化,篇幅原因,下一節(jié)再講
幾個隔離級別的翻譯是參照網(wǎng)絡(luò)上的定義
參照refer中 https://zhuanlan.zhihu.com/p/29166694
對應(yīng)關(guān)系如下
7.思考
讀提交和可重復(fù)讀的區(qū)別
實現(xiàn)快照隔離中 講解過MVCC多個版本中狰腌,一個事務(wù)讀取時該選擇哪個版本
參照上述 可見性原則
Lost Update
按照refer中除破,這個似乎用鎖避免并發(fā)寫就可以(http://blog.csdn.net/bluishglc/article/details/5626009),也就是未提交讀的隔離級別就能解決的癌别。
因此感覺文章順序有點問題,這里應(yīng)該放在最前面蹋笼,而且注意 Lost update不是隔離級別展姐,只是一個并發(fā)考慮的問題Lost update與臟寫的區(qū)別
能保證不出現(xiàn)臟寫的隔離級別,不一定保證不會出現(xiàn)Lost update
Lost update的情形中剖毯,兩個事務(wù)都沒有看到對方未commit的數(shù)據(jù)(臟寫是看得到的)個人感受
不同的情形可能都需要深挖圾笨,書籍也有沒有完全的深入講解,點到即止逊谋。
自己知道這些東西就好
8.名詞
隔離標(biāo)準(zhǔn)
讀提交
臟讀桂躏,臟寫
(不)可重復(fù)讀
快照隔離
MVCC(multi version concurrency control)
lost updates
幻讀
9.refer
http://www.reibang.com/p/a84e4f41a2aa
lost update相關(guān)
http://blog.csdn.net/bluishglc/article/details/5626009
https://zhuanlan.zhihu.com/p/29166694
幻讀
這篇博客寫的太厲害了 https://ggaaooppeenngg.github.io/zh-CN/2017/04/16/SQL%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB/