一、事務的四大特性(ACID)
1.原子性
原子性是指事務包含的所有操作要么全部成功,要么全部失敗回滾,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。
2.一致性
一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之后都必須處於一致性狀態。
拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那么不管A和B之間如何轉賬,轉幾次賬,事務結束后兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
3.隔離性
隔離性是當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個並發事務之間要相互隔離。
即要達到這么一種效果:對於任意兩個並發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,這樣每個事務都感覺不到有其他事務在並發地執行。
4.持久性
持久性是指一個事務一旦被提交了,那么對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。
二、事務的並發問題
數據庫事務無非就兩種:讀取事務(select)、修改事務(update,insert),在沒有事務隔離的時候,多個事務同一時刻對同一數據操作可能會影響到最終結果,可能會產生以下四種情況:
1.兩個更新事務同時修改同一條數據時,會發生很嚴重的情況,會造成更新數據的丟失。 2.一個更新事務在更新一條數據時,另一個讀取事務讀取了還沒有提交的更新數據,這種情況會出現讀取到臟數據。 3.一個讀取事務在讀取一條數據時,另一個更新事務同時修改了這條數據,這樣會出現不可重復讀。 4.一個讀取事務在讀取數據時,另一個事務插入了一條數據,這樣可能多讀出一條數據,出現幻讀。
以上的這四種情況,前三種是對同一條數據的並發操作,對程序的結果可能產生致命影響。綜合以上四種情況可以大致這樣簡單的理解:
1.修改時允許修改(丟失數據)
2.修改時允許讀取(臟讀)
3.讀取時允許修改(不可重復讀)
4.讀取時允許插入(幻讀)
因為不同的系統允許不同級別的情況,所以就出現了事務隔離這個東西,來允許我們設定數據庫的並發行為。
總結如果不考慮事務的隔離性,會發生以下幾種問題:
1.臟讀(針對未提交的數據)
臟讀是指在一個事務讀取了另一個未提交的事務中的數據
2.不可重復讀(針對修改操作) 不可重復讀的意思就是前后兩次讀取的結果不一樣(******)
不可重復讀是指在對於數據庫中的某個數據,一個事務范圍內多次查詢卻返回了不同的數據值,這是由於在查詢間隔,被另一個事務修改並提交了,導致了同一事務中查詢到的結果不一樣。
不可重復讀和臟讀的區別:臟讀是某一事物讀取了另一個事務未提交的臟數據,而不可重復讀則是讀取了前一事務提交的數據
3.幻讀(針對增刪操作)
幻讀是一個事務在讀取數據時,另一個事務插入了新數據,讀取數據的時候會多讀出數據,產生幻覺一樣,這就是幻讀
幻讀和不可重復讀都是讀取了另一條已經提交的事務(臟讀不是這樣),所不同的是不可重復讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)
三、MySQL數據庫的四種事務隔離級別
Read Uncommitted(讀取未提交內容)
在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。本隔離級別很少用於實際應用,因為它的性能也不比其他級別好多少。讀取未提交的數據,也被稱之為臟讀(Dirty Read)
Read Committed(讀取提交內容) 因為讀取了另一個事務提交的數據,所以前后查詢數據是不一樣的,所以造成不可重復讀
這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。它滿足了隔離的簡單定義:一個事務只能看見已經提交事務所做的改變。
這種隔離級別 也支持所謂的不可重復讀(Nonrepeatable Read),因為同一事務的其他實例在該實例處理其間可能會有新的commit,所以同一select可能返回不同結果
Repeatable Read(可重復讀) 可重復讀就是讀取的數據和之前一樣
這是MySQL的默認事務隔離級別,同一事務的多個實例在並發讀取數據時,會看到同樣的數據。不過理論上,這會導致另一個棘手的問題:幻讀 (Phantom Read)。
簡單的說,幻讀指當用戶讀取某一范圍的數據行時,另一個事務又在該范圍內插入了新行,當用戶再讀取該范圍的數據行時,會發現有新的“幻影” 行。
Serializable(可串行化)
這是最高的隔離級別,它通過強制事務排序,使之不可能相互沖突,從而解決幻讀問題。簡言之,它是在每個讀的數據行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭
在MySQL中,實現了四種隔離級別,分別可能產生問題如下所示:
事務隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
讀未提交(read-uncommitted) | 是 | 是 | 是 |
讀已提交(read-committed) | 否 | 是 | 是 |
可重復讀(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,當然級別越高,執行效率就越低。
像Serializable這樣的級別,就是以鎖表的方式(類似於Java多線程中的鎖)使得其他的線程只能在鎖外等待,所以平時選用何種隔離級別應該根據實際情況。
在MySQL數據庫中默認的隔離級別為Repeatable read (可重復讀)
在MySQL數據庫中,支持上面四種隔離級別,默認的為Repeatable read (可重復讀);
而在Oracle數據庫中,只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,其中默認的為Read committed級別
在MySQL數據庫中查看當前事務的隔離級別: select @@tx_isolation;