数据库事务隔离标准分析
1 概述與背景
這是數據庫事務原理和工程實踐系列文章的第一篇,本文主要在Jim Gray的論文<A Critique of ANSI SQL Isolation Levels>基礎上分析關系數據庫的事務隔離級別標準和不同隔離級別情況下的行為。第2節主要討論ANSI標準的下的隔離級別,第3節主要討論基于悲觀鎖實現的事務隔離級別,第4節主要討論基于多版本技術的事務隔離,最后總結排序本文討論到的各個隔離級別。
ACID是關系數據庫的一組重要特性,其中Isolation(隔離性)描述了數據庫允許多個并發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務并發時由于交錯執行而導致數據的不一致。在最極端的情況下,數據庫完全串行化執行每一個事務,所有事務之間遵守全序關系,在這種情況下,不存在并發事務間的隔離問題,但是在實際工程實踐中,處于對數據庫性能吞吐量的考慮,允許多個事務之間按照一定的規則,打破串行話的全序關系,ANSI SQL Isolation Levels即規定了這種“規則”,通過將隔離性劃分為4個級別,來換取多層級的事務間并發能力(即數據庫的吞吐能力)。
注,本文內容融入了作者個人的理解,并沒有嚴格遵守<A Critique of ANSI SQL Isolation Levels>原文的內容;其中cursor stability隔離級別將在后續文章中討論,快照隔離級別與ANSI標準異象的比較也有所不同。
2 ANSI事務隔離級別
ANSI SQL-92標準(http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt)將數據庫并發事務間的隔離性行為劃分為3種"異象(phenomena)",從低到高的自然語言定義依次為:
P1?臟讀 ("Dirty read"): SQL-transaction T1 modifies a row. SQL- transaction T2 then reads that row before T1 performs a COMMIT. If T1 then performs a ROLLBACK, T2 will have read a row that was never committed and that may thus be considered to have never existed.?
P2 不可重復讀 ("Non-repeatable read"): SQL-transaction T1 reads a row. SQL- transaction T2 then modifies or deletes that row and performs a COMMIT. If T1 then attempts to reread the row, it may receive the modified value or discover that the row has been deleted.?
P3 幻讀 ("Phantom"): SQL-transaction T1 reads the set of rows N that satisfy some <search condition>. SQL-transaction T2 then executes SQL-statements that generate one or more rows that satisfy the <search condition> used by SQL-transaction T1. If SQL-transaction T1 then repeats the initial read with the same <search condition>, it obtains a different collection of rows.
通過依次禁止這三種異象,ANSI確定了4種標準隔離級別,如下表所示:
| 級別 | P1(臟讀) | P2(不可重復讀) | P3(幻讀) |
| Read Uncommitted | 允許 | 允許 | 允許 |
| Read Committed | 禁止 | 允許 | 允許 |
| Repeatable Read | 禁止 | 禁止 | 允許 |
| (Anomaly) Serializable | 禁止 | 禁止 | 禁止 |
| Note: The exclusion of these penomena or SQL-transactions executing at isolation level SERIALIZABLE is a consequence of the requirement that such transactions be serializable. | |||
如標準文檔所述,禁止了P1/P2/P3異象的事務即滿足Serializable級別,但矛盾的是,標準文檔中對Serializable又做了如下說明:
| The execution of concurrent SQL-transactions at isolation level SERIALIZABLE is guaranteed to be serializable. A serializable execution is defined to be an execution of the operations of concurrently executing SQL-transactions that produces the same effect as some serial execution of those same SQL-transactions |
它要求多個并發事務執行的效果與某種串行化執行的效果一致,但是僅僅禁止P1/P2/P3異象,并不一定能夠保證“serial execution”的效果,因此論文中將ANSI Serializable稱為Anomaly Serializable。
P1/P2/P3的形式化描述
根據標準文檔的定義,可以將這三種異象使用形式化語言描述如下,稱為A1/A2/A3(其中w1[x]表示事務1寫入記錄x,r1表示事務1讀取記錄x,c1表示事務1提交,a1表示事務1回滾,r1[P]表示事務1按照謂詞P的條件讀取若干條記錄,w1[y in P]表示事務1寫入記錄y滿足謂詞P的條件):
A1 臟讀:w1[x] ... r2[x] ... (a1 and c2 in any order)
A2 不可重復讀:r1[x] ... w2[x] ... c2 ... r1[x] ... c1
A3 幻讀:r1[P] ... w2[y in P] ... c2 ... r1[P] ... c1
上述A1/A2/A3形式化描述,根據標準定義的P1/P2/P3異象的自然語言描述轉化而來,但是ANSI標準定義的異象只針對了單個記錄或謂詞描述,對于多條記錄需滿足業務一致性的場景并未能覆蓋(比如兩個賬戶間轉賬要求余額總和不變),舉例如下:
H1:r1[x=50]w1[x=10]?r2[x=10]r2[y=50]?c2 r1[y=50]w1[y=90] c1
事務1執行賬戶x向賬戶y轉賬40,事務2讀取到了進行到了一半的事務1(Read Uncommitted),破壞了余額總和的一致性
因為事務1并未回滾,H1的行為并不符合A1的形式化定義
H2:r1[x=50]?r2[x=50]w2[x=10]r2[y=50]w2[y=90] c2?r1[y=90]?c1
事務2執行賬戶x向賬戶y轉賬40,事務1在事務2提交前后讀取到了破壞余額總和一致性的數據(Unrepeatable Read)
因為事務1并未重復讀取記錄x,H2的行為并不符合A2的形式化定義
H3:r1[P]?w2[insert y to P] r2[z] w2[z] c2?r1[z]?c1
事務2增加新雇員并更新雇員總數z,事務1在事務2提交前后讀取到了破壞雇員列表與雇員總數的一致性的數據(Phantom)
因為事務1并未重復讀取謂詞P指定的數據集合,H3的行為并不符合A3的形式化定義
因為要增強對上述H1/H2/H3異象的約束,論文將A1/A2/A3的形式化描述稱為“狹義的描述(strict interpretations)”,然后增加了“廣義的描述(broad interpretation)”,去除了strict interpretations中對事務提交、回滾和數據讀取范圍的約束,只保留事務之間讀寫的時序關系,即事務之間只要包含如下時序的操作,即可能產生包含H1/H2/H3在內的異象,如下:
P1 臟讀:w1[x] ... r2[x] ... ((c1 or a1) and (c2 or a2) in any order)
P2 不可重復讀:r1[x] ... w2[x] ...?((c1 or a1) and (c2 or a2) in any order)
P3 幻讀:r1[P] ... w2[y in P]?...?((c1 or a1) and (c2 or a2) in any order)
在上述形式化描述下,禁止P1即可禁止H1,禁止P1/P2即可禁止H2,禁止P1/P2/P3即可禁止H3。至此,ANSI標準隔離級別定義的三種異象,可以被擴展為適用范圍更廣的的P1/P2/P3的形式化定義,這種隔離級別定義被論文稱之為“phenomena-based”,即基于“異象”的隔離級別定義。
3 基于鎖的事務隔離
在上一節的討論中,P1/P2/P3這三種形式化定義指出了三個不同級別的異象,但是并沒有與實際的工程實踐相關聯,在本小節中,我們將介紹基于鎖(lock base)的事務隔離實現,并且將不同的加鎖行為與上述三種異象關聯起來討論。
在討論加鎖行為之前,需要定義如下幾種讀寫和鎖的操作:
Predicate lock 謂詞鎖(gap鎖):Locks on all data items satisfying the search condition
Well-formed Writes 合法write:Requests a Write(Exclusive) lock on each data item or predicate before writing
Well-formed Reads 合法read:Requests a Read(share) lock on each data item or predicate before reading
Long duration locks 長周期鎖:Locks are held until after the transaction commits or aborts
Shord duration locks 短周期鎖:Locks are released immediately after the action completes
通過組合上述讀寫鎖操作,我們能夠構建不同級別的事務隔離標準。因為“No Well-formed Writes”或“Short duration write locks”的保護等級可能造成dirty write,它的約束已經低到難以找到實際應用場景,我們將其忽略,因此所有寫入操作都使用“Well-formed Writes,?Long duration Write locks”,通過對讀取操作應用不同的保護等級,得到4種隔離級別,使用locking前綴與ANSI隔離級別區分,如下表所示:
| Read Lock | Write Lock | |
| Locking Read Uncommited | none required | Well-formed Writes, Long duration Write locks |
| Locking Read Commited | Well-formed Reads, Short duration read lock | Well-formed Writes, Long duration Write locks |
| Locking Repeatable Read | Well-formed Reads, Long duration data-item Read locks, Short duration Read Predicate locks | Well-formed Writes Long duration Write locks |
| Locking Serializable | Well-formed Reads, Long duration Read locks | Well-formed Writes Long duration Write locks |
將locking標記的四種隔離級別與ANSI隔離級別對比:
Well-formed Reads,?Short duration read lock?禁止了 P1發生,r2[x]將被讀鎖阻塞,直到事務1提交或回滾
Well-formed Reads,?Long duration data-item Read locks,?Short duration Read Predicate locks?禁止了P2發生,w2[x]將被寫鎖阻塞,直到事務1提交或回滾
其中Short duration Read Predicate locks的作用論文中并沒有說明,實際上它保護了r[P]的一致性,保證r[P]讀取到的多行數據是一個“well-formed history”
Well-formed Reads,?Long duration Read locks?禁止了P3發生,w2[y in P]將被謂詞寫鎖阻塞,直到事務1提交或回滾
如上所述,Lock Base的隔離級別能夠完全覆蓋ANSI基于異象的隔離級別約束,論文中也稱“phenomena-based”是“disguised versions of locking”。
4 基于快照的事務隔離
對于基于鎖實現事務隔離的數據庫,讀寫、寫寫事務之間也可能因為鎖沖突而被阻塞,數據庫的整體吞吐能力受到比較大的限制,特別是在目前多核的CPU條件下,難以充分發揮計算能力。因此現代關系型數據庫和NewSQL,比如MySQL/Oracle/PostgreSQL/OceanBase/TiDB等,都使用多版本并發控制(mvcc)技術,來實現事務隔離,它的核心設計思想是,為數據的每次修改保存一個用時間戳標記的版本,數據讀取不需要加鎖,而是在讀取事務開始的時候獲取當前時間戳(snapshot),對于每條數據,將版本號小于snapshot的最大已提交版本的內容作為讀取結果返回。
Snapshot Isolation保證只讀事務與讀寫事務相互不阻塞,只讀事務通過讀取合法的歷史快照,保證了讀取到的數據的一致性,我們在快照隔離下與A1/A2/A3逐個對比分析:
P1描述的w1[x] ... r2[x] ...操作時序不可能出現,因為在快照隔離下,實際的操作時序為w1[x] ... r2[last version of x] ...,因此可知快照隔離禁止P1
P2描述的r1[x] ... w2[x] ... 它實際的操作時序為r1[x] ... w2[new version of x] ...,可以知道快照隔離也禁止了P2。至此,我們可以確定快照隔離的效果至少大于Read Committed
P3描述的r1[P] ... w2[y in P] ... 它實際的操作時序為r1[P] ... w2[new version of y in P] ...,可以知道快照隔離也禁止了P3,達到了第2小節中ANSI的Anomaly?Serializable級別
但是,從上一小節基于鎖的隔離級別定義來分析,快照隔離的安全級別可能并沒有那么高,我們來看如下兩種異象的形式化描述:
A5A Read Skew: r1[x]...w2[x]...w2[y]...c2...r1[y]...(c1 or a1)
A5B Writer Skew: r1[x]...r2[y]...w1[y]...w2[x]...(c1 and c2 occur)
擴展的Write Skew(并非來自原文):r1[P]...r2[P]...w1[x]...w2[y]...(c1 and c2 occur)
快照隔離性高于Read Committed:第一,考慮到快照隔離讀已提交的數據版本的特性,禁止了P1,因此保證至少不低于Read Committed。第二,A5A的Read Skew異象符合P2的定義,并且從一致性的角度分析,事務1對x和y的讀取的兩個值不在線性的歷史中,可能會違背某種外部約束(比如保證x+y的和為一個常量),因此Read Committed隔離級別下允許A5A Read Skew異象。總和以上兩點,我們可以得出結論,快照隔離性高于Read Committed。
快照隔離性與Repeatable Read不相容:考慮到快照隔離能夠保證讀取到的數據在一個一致的歷史快照上,禁止了P1/P2/P3,因此保證不低于ANSI的Anomaly?Serializable級別;但是,另一方面,經典的快照隔離對于多寫沖突是基于First- committer-wins的處理方式,依賴沖突的事務間至少修改同一條記錄(現代快照隔離有更優的SSI,我們將在后續的文章中介紹)無法避免上述A5B Write Skew的兩種異象,而基于鎖事項的Repeatable Read級別卻可以禁止A5B。快照隔離與Repeatable Read雙方禁止的異象,有可能在對方出現,因此他們的隔離性無法相比較。
5 總結
從前面幾個小節的隔離性分析來看,我們可以得到如下幾種隔離級別的關系:
| Read Uncommitted?<?Read Committed?<?(Repeatable Read?><?Snapshot)?<?Serializable |
本文首先介紹了ANSI基于“異象”的隔離級別標準,并分析了其狹義和廣義的描述;然后介紹了基于鎖的隔離級別標準,與ANSI隔離級別進行了比較;最后分析快照隔離級別,在ANSI隔離級別標準基礎上,提出了兩種新的“異象”,得出快照隔離在幾種標準隔離級別特性中的位置。
來自 “ ITPUB博客 ” ,鏈接:http://blog.itpub.net/31556421/viewspace-2218934/,如需轉載,請注明出處,否則將追究法律責任。
轉載于:http://blog.itpub.net/31556421/viewspace-2218934/
總結
以上是生活随笔為你收集整理的数据库事务隔离标准分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu下使用ROS moveit
- 下一篇: 今天一天学习的MYSQL语句,立此存照