參考Wikipedia: http://en.wikipedia.org/wiki/Isolation_(database_systems)#Repeatable_reads_.28phantom_reads.29
Isolation的理想狀態是:the result of Transaction A is invisible to Transaction B until Transaction A is completed。不過這麼一來,只有Serializable級別才能滿足要求,即Transactions是一個接一個地執行,不允許Transactions的並行。如果想要Transactions的並行,那麼就不可能有絕對的isolation。爲此,ANSI/ISO制定的SQL標準給Isolation分了4個級別,由高到低分別是:
- Serializable
- Repeatable Read (Phantom Read)
- Read Committed (Nonrepeatable Read)
- Read Uncommitted (Dirty Read)
級別越高的,isolation性越好,而低級別的isolation會有各種各樣的併發問題(如上面括號中所示)。下面一一介紹。
1. Read Uncommitted
Transaction 1可以查看Transaction 2未提交的操作結果,這會導致Dirty Read的問題,如下圖所示:
Transaction 1的執行的第二個query會讀到Transaction 2中update的結果,如果Transaction 2 rollback的話,這個讀到的數據明顯是錯誤的。
2. Read Committed
如果我們強制Transaction 1只能讀Transaction 2中已經commit的update,就可以解決Dirty Read的問題。強制的手段是:每次query(比如SELECT)時,都會獲取當前數據庫(或者可能只是query涉及的表)的一個snapshot,query從這個snapshot中獲得結果。沒有commit的update不會在snapshot中出現,所以就不會被query到。
這樣允許Transaction 2並行執行,且不會影響Transaction 1。當Transaction 1 commit的時候,DBMS檢測是否有衝突(比如Transaction 1也update了Transaction 2中update的row),如果執行的結果等同於順序執行Transaction 1和 Transaction 2 ,則認爲沒有衝突。
但這樣也會有問題:Nonrepeatable Read,即可能執行同一query兩次而出現不同的結果,我們認爲這是不符合一致性原則的。
3. Repeatable Read
解決Nonrepeatable Read問題的辦法是給Transaction 1中的SELECT涉及的行加一個read lock。
鎖的排斥功能很明確:read locks不會block read locks,只block write locks;write locks block both read locks and write locks。
不過會有新的問題:phantom read。
當Transaction 1執行一個range query(範圍查詢,如like, between等)時,只會爲涉及到的row加read lock,如果插入一個新row在range之內,第二次range query還是會被讀出。
4. Serializable
強制Transactions按順序執行,所以Transaction之間不可能衝突。強制的手段是給place a read lock on every row the transaction read;同時,如果有range query,還會添加一個range lock。明顯,Serializable會耗費很多的lock overhead。
Serializable相當於在Transaction 1開始時(而不是query開始時)給當前數據庫(或是Transaction涉及的表)take a snapshot,Transaction 1中的所有query都從這一個snapshot中獲取結果。