深入了解事務的隔離機制
深入了解一下事務的隔離機制(在之前資料庫入門階段並沒有提到)我們說了,事務之間是相互隔離互不干擾的,那麼如果出現了下面的情況,會怎麼樣呢:
當兩個事務同時在執行,並且同時在操作同一個數據,這樣很容易出現並發相關的問題,比如一個事務先讀取了某條數據,而另一個事務此時修改了此數據,當前一個事務緊接著再次讀取時,會導致和前一次讀取的數據不一致,這就是一種典型的數據虛讀現象。
因此,為了解決這些問題,事務之間實際上是存在一些隔離級別的:
- ISOLATION_READ_UNCOMMITTED(讀未提交):其他事務會讀取當前事務尚未更改的提交(相當於讀取的是這個事務暫時快取的內容,並不是資料庫中的內容)
- ISOLATION_READ_COMMITTED(讀已提交):其他事務會讀取當前事務已經提交的數據(也就是直接讀取資料庫中已經發生更改的內容)
- ISOLATION_REPEATABLE_READ(可重複讀):其他事務會讀取當前事務已經提交的數據並且其他事務執行過程中不允許再進行數據修改(注意這裡僅僅是不允許修改數據)
- ISOLATION_SERIALIZABLE(串列化):它完全服從ACID原則,一個事務必須等待其他事務結束之後才能開始執行,相當於挨個執行,效率很低
我們依次來看看,不同的隔離級別會導致什麼問題。首先是讀未提交
級別,此級別屬於最低級別,相當於各個事務共享一個快取區域,任何事務的操作都在這裡進行。那麼它會導致以下問題:
也就是說,事務A最後得到的實際上是一個毫無意義的數據(事務B已經回滾了)我們稱此數據為”臟數據”,這種現象稱為臟讀
我們接著來看讀已提交
級別,事務只能讀取其他事務已經提交的內容,相當於直接從數據中讀取數據,這樣就可以避免臟讀問題了,但是它還是存在以下問題:
這正是我們前面例子中提到的問題,雖然它避免了臟讀問題,但是如果事件B修改並提交了數據,那麼實際上事務A之前讀取到的數據依然不是最新的數據,直接導致兩次讀取的數據不一致,這種現象稱為虛讀也可以稱為不可重複讀
因此,下一個隔離級別可重複讀
就能夠解決這樣的問題(MySQL的默認隔離級別),它規定在其他事務執行時,不允許修改數據,這樣,就可以有效地避免不可重複讀的問題,但是這樣就一定安全了嗎?
這裡僅僅是禁止了事務執行過程中的UPDATE操作,但是它並沒有禁止INSERT這類操作,因此,如果事務A執行過程中事務B插入了新的數據,那麼A這時是毫不知情的,比如:
兩個人同時報名一個活動,兩個報名的事務同時在進行,但是他們一開始讀取到的人數都是5,而這時,它們都會認為報名成功後人數應該變成6,而正常情況下應該是7,因此這個時候就發生了數據的幻讀現象。
因此,要解決這種問題,只能使用最後一種隔離級別串列化
來實現了,每個事務不能同時進行,直接避免所有並發問題,簡單粗暴,但是效率爆減,並不推薦。
最後總結三種情況:
- 臟讀:讀取到了被回滾的數據,它毫無意義。
- 虛讀(不可重複讀):由於其他事務更新數據,兩次讀取的數據不一致。
- 幻讀:由於其他事務執行插入刪除操作,而又無法感知到表中記錄條數發生變化,當下次再讀取時會莫名其妙多出或缺失數據,就像產生幻覺一樣。
(對於虛讀和幻讀的區分:虛讀是某個數據前後讀取不一致,幻讀是整個表的記錄數量前後讀取不一致)
最後這張圖,請務必記在你的腦海,記在你的心中,記在你的全世界: