前面我們介紹了關(guān)于隔離型的內(nèi)容,如果使用弱隔離性時,數(shù)據(jù)庫性能較好,但可能會出現(xiàn)事務(wù)并行的問題;如果使用事務(wù)的串行化,事務(wù)的并行執(zhí)行的結(jié)果和串行是一樣的,但性能較差。因此,有沒有一種串行化的方法,能夠結(jié)合這兩種方式的優(yōu)點呢:這就是本節(jié)要介紹的串行化的鏡像隔離(serializable snapshot isolation,SSI)。
串行化的鏡像隔離
我們之前提到的兩種串行化的方法:單線程的串行化,和兩階段鎖,在本質(zhì)上屬于一種悲觀鎖的實現(xiàn)。悲觀鎖指的是我們假設(shè)每個事務(wù)都有可能引起并發(fā)性的問題,因此要等待環(huán)境安全之后才進行事務(wù)的執(zhí)行。本節(jié)我們介紹的SSI,在本質(zhì)上屬于樂觀鎖,也就是它假設(shè)事務(wù)的并發(fā)執(zhí)行都是正常的,只有在事務(wù)提交前,才會檢查是否有違背隔離性的事情發(fā)生,會中止并重試該事務(wù)。
樂觀鎖的優(yōu)點是當數(shù)據(jù)空間足夠稀疏時,事務(wù)發(fā)生并發(fā)沖突的可能性較低,事務(wù)執(zhí)行的效率是很高的;但是如果事務(wù)重試的比例很高,對性能的影響較大。
基于過期假設(shè)的決定
一些事務(wù)的執(zhí)行是基于事務(wù)開始時的假設(shè),比如醫(yī)生離崗的問題,假設(shè)是當前有兩個醫(yī)生在值班,也就是事務(wù)中的查詢和數(shù)據(jù)寫入。但是當事務(wù)提交時,這個假設(shè)的結(jié)論可能會變化,因此有兩種檢測假設(shè)結(jié)論是否變化的方式:
- 檢測過期的MVCC數(shù)據(jù)對象的讀?。ㄎ刺峤坏膶懭氚l(fā)生在讀取之前);
- 檢測數(shù)據(jù)寫入影響了先前的讀?。▽懭氚l(fā)生在讀取之后)。
檢測過期的MVCC讀取
由于我們使用了snapshot隔離性,事務(wù)讀取到的是其他事務(wù)未提交之前的snapshot。在醫(yī)生離崗的例子中,通過MVCC判斷是否發(fā)生了事務(wù)的并發(fā)沖突的示意圖如下:

事務(wù)43在讀取時,已知事務(wù)42已經(jīng)要寫入該數(shù)據(jù)的值,但未提交;當事務(wù)43提交時,判斷事務(wù)42是否已經(jīng)提交,如果已經(jīng)提交的話,則事務(wù)43中止并重試。
為什么在提交時才判斷,而不是在事務(wù)43發(fā)現(xiàn)事務(wù)42已經(jīng)要寫入該數(shù)據(jù)值就中止呢?因為如果事務(wù)43僅讀取數(shù)據(jù)的話,它并不需要被中止,在事務(wù)43提交之前,事務(wù)管理器并不知道它是否要修改數(shù)據(jù)。同時,在事務(wù)43提交時,事務(wù)42可能被中止,這樣事務(wù)43就是可以提交的。
檢測寫入影響先前的讀取
第二種方式是記錄下事務(wù)修改數(shù)據(jù)時,已經(jīng)有哪些未提交的事務(wù)在先前已經(jīng)讀取該數(shù)據(jù),中止這些事務(wù)。如下圖所示:

在兩階段鎖中,我們提到了索引范圍鎖(index-range locks),這里也采用類似的方式。假設(shè)我們查詢shift_id=1234的數(shù)據(jù),我們記錄事務(wù)42和43都讀取了該條件的數(shù)據(jù)。當事務(wù)42更新數(shù)據(jù)并提交時,由于它已知事務(wù)43已經(jīng)讀取相同條件的數(shù)據(jù),因為會中止事務(wù)43的執(zhí)行。
SSI的性能
和兩階段鎖相比,SSI的事務(wù)執(zhí)行不會阻塞其他事務(wù)的執(zhí)行,和snapshot隔離性相同,讀寫不會互相阻塞。這使得事務(wù)執(zhí)行的延遲更加穩(wěn)定和可預(yù)測,特別是針對讀取為主的系統(tǒng),性能會提升很多。
和串行執(zhí)行相比,SSI會使用多個CPU核工作,可以很容易的進行水平擴展。甚至當數(shù)據(jù)分區(qū)到多臺機器時,事務(wù)能夠在讀寫多個分區(qū)時,保證不會有并發(fā)問題。
最后,SSI本身的性能和事務(wù)沖突的幾率是有關(guān)系的。