若是說數據在 ACID 特性(帶有了併發控制技術)的保護下會發生不一致的現象,那麼:web
在 ACID 和快照隔離級別技術(多版本)的保護下,是否是數據就必定再也不會產生不一致的現象呢 ?sql
答案是否認的。數據庫系統中數據的異常,在多種併發控制技術中已經被解決,但這不代表全部的異常都已經被解決,更不代表再也不有新的異常被發現。數據庫
咱們知道,數據庫併發控制技術中有一個大名鼎鼎的技術,稱爲快照隔離( Snapshot Isolation ),這項技術解決了讀寫衝突,在保證數據不會產生前面兩節提到的讀異常和寫異常的狀況下,使得讀寫互不阻塞(兩階段鎖技術讀寫操做互相阻塞),提升了併發度。併發
注意咱們這裏談到的多版本是「 multi-version ,簡稱 MV 」,其相對於「 single-valued ,簡稱 SV 」,這個多版本是快照隔離併發控制技術中的數據項有多個版本,其含義僅此而已。快照隔離併發技術是( Multiversion concurrency control , MVCC )技術的一種,二者都要基於「數據項存在多個版本」,但與多版本併發控制技術相比二者是有區別的(具體區別參見 2.2 節對併發控制技術的探討)。ide
快照隔離併發控制技術的缺點,是並不能真正保證事務爲「可序列化的」,即事務間的併發操做依舊會引起數據異常現象,可是這裏的數據異常現象區別於前面提到的各類異常現象,其異常現象是「業務的邏輯語義」引起的,即除了抽象的讀寫操做,數據間還應該知足必定語義,即約束( constraint )。post
在快照隔離併發控制技術中併發的事務因不知足約束而發生的異常,成爲「寫偏序( Write Skew )」,這樣的異常有兩種,參見 1- 4 。ui
表 1-4 寫偏序異常的兩種狀況 [1] spa
兩個事務寫偏序 postgresql |
三個事務寫偏序 xml |
||||
T1 |
T2 |
T1 |
T2 |
T3 |
|
t0 |
x ← SELECT COUNT( ? ) FROM doctors WHERE on ? call = true |
x ← SELECT current_batch |
|||
t1 |
x ← SELECT COUNT( ? ) FROM doctors WHERE on ? call = true |
INCREMENT |
|||
t2 |
IF x ≥ 2 THEN UPDATE doctors SET on ? call = false WHERE name = Alice |
Commit |
|||
t3 |
IF x ≥ 2 THEN UPDATE doctors SET on ? call = false WHERE name = Bob |
x ← SELECT current_batch |
|||
t4 |
Commit |
SELECT SUM(amount) FROM WHERE batch = x ? 1 |
|||
t5 |
Commit |
COMMIT |
|||
t6 |
INSERT INTO receipts |
||||
t7 |
COMMIT |
說明:
q 表格頭兩行,代表寫偏序異常現象的兩種狀況,分別是由兩個事務引起異常、三個事務引起異常。
q 表格第一列,時間值列,代表時間值在逐漸增加,即 t0<t1<t2<t3<t4<t5<t6<t7 。
q 對於每一種異常現象,都分爲 2 個列,分別是兩個併發的事務,各自命名爲 T1 事務和 T2 事務。
q 對於二個事務引起的異常現象(簡單寫偏序, Simple Write Skew ): 按照時間順序, T1 事務在 t0 時刻讀取了在打電話的值班醫生個數, T2 事務在 t1 時刻也讀取了在打電話的值班醫生個數。事務 T1 在 t2 時刻進行判斷:若是在打電話的值班醫生個數大於等於 2 人則請 Alice 中止打電話。事務 T2 在 t3 時刻進行判斷:若是在打電話的值班醫生個數大於等於 2 人則請 Bob 中止打電話。而後事務 T1 和 T2 分別提交。若是在這種併發的狀況下,容許事務 T1 和 T2 都提交成功,則 t6 時刻, Alice 和 Bob 都中止了打電話。若是串行執行事務,先執行事務 T1 後執行事務 T2 , Alice 會中止打電話但 Bob 不會中止,這與前一種狀況的結果不一樣;若是先執行事務 T2 後執行事務 T1 , Bob 會中止打電話但 Alice 不會中止,這與前一種狀況的結果也不一樣;這代表前一種併發執行是非序列化的,即事務 T1 、 T2 併發時違反了約束( 約束爲 :若是同時打電話的人數大於等於 2 人則請 Alice 或 Bob 其中一我的中止打電話直到同時打電話的人數少於 2 人)發生了寫偏序異常現象。對於簡單寫偏序,能夠用一個形象化的圖表示,參見圖 1-1 。
q 對於三個事務引起的異常現象( Batch Processing ): 對於這種狀況,後兩個併發更新事務 T3 和 T2 是可串行化的且不存在任何異常,可是一個只讀事務 T1 出如今某個時刻卻可能正好形成問題。所出現的問題是這樣的,當事務 T3 提交時, T2 處於活躍狀態,這時,事務 T1 啓動要讀取事務 T2 和 T3 涉及的數據( current_batch 和 receipts ),這時,事務 T1 的快照包括了事務 T3 的插入後的結果(由於 T3 已經提交);可是,事務 T2 沒有提交,它的插入操做數據不包含在事務 T1 的快照中。在優先圖(如圖 1-2 )中會形成一個環(有關如何造成這樣的環), 說明這樣的 調度是非可串行化的 。
圖 1-1 兩個事務引起的異常現象優先圖
圖 1-2 三個事務引起的異常現象優先圖
本節所述的這兩種狀況,若是使用優先圖表示,均可以在參與操做的事務之間,畫出一個環,存在環說明: 調度是非可串行化的。 爲解決這樣的問題,這就要求數據庫引擎必須在事務提交時而不是在快照上檢查完整性約束以免本節所述的不一致現象。
注意:
更多的寫偏序異常示例,能夠參見: https://wiki.postgresql.org/wiki/SSI#Read_Only_Transactions
[1] 示例源自論文: Dan R. K. Ports , Kevin Grittner , Serializable Snapshot Isolation in PostgreSQL
備註:轉載自:http://www.tuicool.com/articles/bEBRzyM