並發事務處理能大大增加數據庫資源的利用率,提高數據庫系統的事務吞吐量,從而可以支持更多用戶。
但是同時會帶來諸多問題
1、更新丟失(Lost Update)
兩個或者多個事務同時選擇同一行數據,都基於最初選定的值更新該行,由於每個事務都不知道其它事務的存在,就會發生更新丟失的問題。最后提交的更新覆蓋了之前其它事務所做的更新。
2、臟讀(Dirty Reads)
一個事務正在對一條記錄進行修改,這個事務完成並提交前,這條記錄的數據就處於不一致的狀態。此時,另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“臟”數據,
並依據此做了進一步的處理,就會產生對未提交的數據的依賴關系。這種現象就叫做“臟讀”。
總結:事務A讀取到了事務B已經修改但尚未提交的數據,還在這個數據基礎上做了操作。此時,如果B事務回滾,A讀取的數據無效,不符合一致性要求。
3、不可重復讀(Non-Repeatable Reads)
一個事務在讀取某些數據后的某個時間,再次讀取以前讀過的數據,卻發現其讀出的數據已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做“不可重復讀”,重復讀到的是不同的數據。
總結:事務A讀取到了事務B已經提交的修改數據,不符合隔離性。
4、幻讀(Phantom Reads)
一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱為“幻讀”。
舉個例子:比如事務A第一次查詢到表Student中有a、b、c三條數據,然后事務B向里面添加一條d數據,事務A再按照原來的查詢條件查詢發現查詢出四條數據,這讓事務A產生幻想,之前
明明就只有三條數據,為什么現在卻有四條數據了呢?
總結:事務A讀取到了事務B提交的新增數據,不符合隔離性。
解決方案:
解決更新丟失主要有以下兩個方式:
- 使用事務+鎖定讀,也就是for update
- 不使用事務,用CAS自旋來操作
臟讀、不可重復讀和幻讀其實都是數據庫讀一致性問題,必須由數據庫提供一定的事務隔離機制來解決。那有哪些隔離級別呢?主要有以下四種隔離級別:
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|
讀未提交(Read uncommitted) | 不能解決 | 不能解決 | 不能解決 |
讀已提交(Read committed) | 可以解決 | 不能解決 | 不能解決 |
可重復讀(Repeatable read) | 可以解決 | 可以解決 | 不能解決 |
可串行化(Serializable) | 可以解決 | 可以解決 | 可以解決 |
假設現在有兩個事務,事務A和事務B,那么上面的四種隔離級別是什么意思呢?
1、讀未提交(Read uncommitted)
字面意思,就是可以讀到別的事務未提交的數據,也就是事務A可以讀取事務B未提交的數據,這種情況肯定可能會導致臟讀、不可重復度和幻讀。
2、讀已提交(Read committed)
字面意思,就是只可以讀到別的事務已提交的數據,也就是事務A只可以讀取到事務B已經提交了的數據,那么在B未提交之前的數據是讀取不到的,也就不可能產生臟讀,但是因為事務B已提交的
數據是可以讀取到的,所以可能會導致不可重復讀和幻讀。
3、可重復讀(Repeatable read)
字面意思,就是事務可以重復讀取數據,在事務期間,每次讀取的數據都是一樣的,也就是事務A開啟事務后,讀取了某一個表的5條數據,不管你事務B怎么對這5條數據修改操作,我事務A每次
查詢都是5條一摸一樣的數據,所以是可重復讀的,因此不可能導致臟讀,不可重復讀,但是還是可能導致幻讀的。
4、可串行化(Serializable)
mysql中事務隔離級別為serializable時會鎖表,因此不會出現幻讀的情況,這種隔離級別並發性極低,開發中很少會用到。
擴展:
我們都知道MySQL默認的隔離級別是可重復讀(Repeatable read),那為什么不會產生幻讀呢?
原因:MySQL在可重復讀(Repeatable read)隔離級別的情況下使用了next-key Lock鎖算法,因此可以避免幻讀的產生。next-key Lock鎖,鎖定的不是單個值,而是一個范圍(GAP),
並且鎖定記錄本身。對於行的查詢,都是采用該方法。