在探讨事務中的隔離之前,先回顧一下有關事務的基礎概念
基本概念
資料庫中的“事務處理”是什麼?
這個問題的解答是我們探讨的基礎,我們知道事務是資料庫中喲一個單獨的執行單元(Unit),它通常由進階資料庫操縱語言(例如SQL)或程式設計語言(例如C++和Java等)編寫的使用者程式的執行所引起。當在資料庫中更改資料成功時,在事務中更改的資料便會送出,不再改變。否則,事務就會取消或者復原,使更改無效。
事務滿足四個屬性,就是常說的ACID
- 原子性: 事務是最小的執行機關,不允許分割。事務的原子性確定動作要麼全部完成,要麼完全不起作用;
- 一緻性: 執行事務前後,資料保持一緻;由于并發操作帶來的不一緻性,通常包括以下幾種類型:丢失資料修改,讀“髒”資料,不可重複讀和幻讀(也稱為産生幽靈資料)。
穿插上述這幾個名詞的解釋:
髒讀(Dirty read): 當一個事務正在通路資料并且對資料進行了修改,而這種修改還沒有送出到資料庫中,這時另外一個事務也通路了這個資料,然後使用了這個資料。因為這個資料是還沒有送出的資料,那麼另外一個事務讀到的這個資料是“髒資料”,依據“髒資料”所做的操作可能是不正确的。
丢失修改(Lost to modify): 指在一個事務讀取一個資料時,另外一個事務也通路了該資料,那麼在第一個事務中修改了這個資料後,第二個事務也修改了這個資料。這樣第一個事務内的修改結果就被丢失,是以稱為丢失修改。 例如:事務1讀取某表中的資料A=20,事務2也讀取A=20,事務1修改A=A-1,事務2也修改A=A-1,最終結果A=19,事務1的修改被丢失。
不可重複讀(Unrepeatableread): 指在一個事務内多次讀同一資料。在這個事務還沒有結束時,另一個事務也通路該資料。那麼,在第一個事務中的兩次讀資料之間,由于第二個事務的修改導緻第一個事務兩次讀取的資料可能不太一樣。這就發生了在一個事務内兩次讀到的資料是不一樣的情況,是以稱為不可重複讀。
幻讀(Phantom read): 幻讀與不可重複讀類似。它發生在一個事務(T1)讀取了幾行資料,接着另一個并發事務(T2)插入了一些資料時。在随後的查詢中,第一個事務(T1)就會發現多了一些原本不存在的記錄,就好像發生了幻覺一樣,是以稱為幻讀。
不可重複度和幻讀差別:
不可重複讀的重點是修改,幻讀的重點在于新增或者删除。
- 隔離性: 并發通路資料庫時,一個使用者的事物不被其他事物所幹擾,各并發事務之間資料庫是獨立的;
SQL 标準定義了四個隔離級别:
READ-UNCOMMITTED(讀取未送出): 最低的隔離級别,允許讀取尚未送出的資料變更,可能會導緻髒讀、幻讀或不可重複讀
READ-COMMITTED(讀取已送出): 允許讀取并發事務已經送出的資料,可以阻止髒讀,但是幻讀或不可重複讀仍有可能發生
REPEATABLE-READ(可重複讀): 對同一字段的多次讀取結果都是一緻的,除非資料是被本身事務自己所修改,可以阻止髒讀和不可重複讀,但幻讀仍有可能發生。
SERIALIZABLE(可串行化): 最高的隔離級别,完全服從ACID的隔離級别。所有的事務依次逐個執行,這樣事務之間就完全不可能産生幹擾,也就是說,該級别可以防止髒讀、不可重複讀以及幻讀。

- 持久性: 一個事務被送出之後。它對資料庫中資料的改變是持久的,即使資料庫發生故障也不應該對其有任何影響。
持久化涉及到資料庫的恢複問題
事務的特性(ACID)要牢記哈
有了上面的認識之後,下面來具體看看隔離性:
隔離級别的了解
上面大緻說了說隔離以及隔離級别
概念是了解的結晶,執行個體是幫助了解的工具
下面用一行代碼 來解釋一下四種隔離級别:
create table num(n int) engine=InnoDB;
insert into num(n) values(1);
按照時間順序執行下面事務:
若隔離級别是 “讀未送出” ,則X的值就是2。這時候事務B雖然還沒有送出,但是結果已經被A看到了。是以,Y、Z也都是2。
若隔離級别是“讀送出”,則X是1,Y的值是2。事務B的更新在送出後才能被A看到。是以,Z的值也是2。
若隔離級别是“可重複讀”,則X、Y是1,Z是2。之是以Y還是1,遵循的就是這個要求:事務在執行期間看到的資料前後必須是一緻的。
若隔離級别是“串行化”,則在事務B執行“将1改成2”的時候,會被鎖住。直到事務A送出後,事務B才可以繼續執行。是以從A的角度看,X、Y值是1,Z的值是2。
在具體實作上,資料庫裡面會建立一個視圖,通路的時候以視圖的邏輯結果為準。
在“可重複讀”隔離級别下,這個視圖是在事務啟動時建立的,整個事務存在期間都用這個視圖。
在“讀送出”隔離級别下,這個視圖是在每個SQL語句開始執行的時候建立的。
這裡需要注意的是,“讀未送出”隔離級别下直接傳回記錄上的最新值,沒有視圖概念;而“串行化”隔離級别下直接用加鎖的方式來避免并行通路。
事務隔離的實作
這裡選擇“可重複讀”進行叙述
之前寫過一篇博文:
【資料庫】讨論MySQL日志系統中的更新語句的執行裡面提到了復原日志(undo log):它儲存了事務發生之前的資料的一個版本,可以用于復原,同時可以提供多版本并發控制下的讀(MVCC),也即非鎖定讀。
可重複度 與 MVCC密切相關:
加入有一個值1 首先被修改為2 然後被修改為3 最後被修改為4
在這個過程中:
復原日志大緻情況如下:
目前值是4,但是在查詢這條記錄的時候,不同時刻啟動的事務會有不同的read-view。
如圖中看到的,在視圖A、B、C,D裡面,這一個記錄的值分别是1、2、3、4,同一條記錄在系統中可以存在多個版本,就是資料庫的多版本并發控制(MVCC)。
對于read-view A,要得到1,就必須将目前值依次執行圖中所有的復原操作得到。
同時你會發現,即使現在有另外一個事務正在将4改成5,這個事務跟read-view A、B、C對應的事務是不會沖突的。
基于上面的說明,我們來讨論一下長事務。
長事務意味着系統裡面會存在很老的事務視圖。 由于這些事務随時可能通路資料庫裡面的任何資料,是以這個事務送出之前,資料庫裡面它可能用到的復原記錄都必須保留,這就會導緻大量占用存儲空間。
除了對復原段的影響,長事務還占用鎖資源,是以不建議使用長事務。