一、臟讀(讀到未提交的數據)
在理解臟讀之前,需要理解臟數據的概念。
臟數據是指事務對緩沖池中行記錄的修改,並且還沒有提交。
注意臟數據和臟頁是完全不同的兩種概念,臟頁指的是在緩沖池中已經被修改的頁,但是還沒有刷新到磁盤中,即數據庫實例內存中的頁和磁盤中的頁的數據不一致。臟頁的讀取是非常正常的,臟頁是數據庫實例內存和磁盤異步造成的,這並不影響數據的一致性,臟頁最終會被刷新到磁盤中。
臟讀指的是在不同事務下,當前事務可以讀取到另外事務未提交的數據,簡單來說就是可以讀到臟數據。
和臟頁不同,如果讀到了臟數據,即一個事務讀取到了另外一個事務未提交的事務,顯然違反了了事務的隔離性。
下面舉個例子
轉賬事務 | 取款事務 | |
---|---|---|
1 | 開始事務 | |
2 | 開始事務 | |
3 | 查新賬戶余額為2000 | |
4 | 取款1000元,余額改為1000 | |
5 | 查詢賬戶余額為1000(臟讀) | |
6 | 取款發生錯誤,事務回滾,余額變為2000 | |
7 | 轉入2000,余額變為3000(1000+2000) | |
8 | 提交事務 | |
備注 | 正常邏輯此時賬戶為4000元 |
臟讀現象在實際生產環境並不常見,因為臟讀發生的條件是需要事務的隔離級別為READ UNCOMMITTED,而目前絕大多數數據庫都至少設置成READ COMMITTED。InnoDB存儲引擎默認事務隔離級別為READ REPEATABLE。
二、不可重復讀(前后讀取到的數據內容不一致)
不可重復讀指在一個事務內多次讀取同一數據,在這個事務還沒結束時,另一個事務也訪問該數據,並修改了改數據,因此在第一個事務中的兩次讀取數據之間,由於第二個事務的修改,第一個事務兩次讀取到的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀取的數據是不一樣的,這種情況稱為不可重復讀。
注意和臟讀的區別:臟讀讀取到的是未提交的數據,不可重復讀讀到的是已提交的數據,但違反了數據庫事務的一致性
舉個例子:
事務A | 事務B | |
---|---|---|
1 | 開始事務 | |
2 | 第一次查詢,小明為20歲 | |
3 | 開始事務 | |
4 | 其他操作 | |
5 | 更改小明年齡為25 | |
6 | 提交事務 | |
7 | 第二次查詢,小明年齡為25 | |
備注 | 兩次讀取到數據不一致 |
在InnoDB存儲引擎中,默認事務隔離級別為READ REPEATABLE,通過使用Next-Key Lock算法來避免不可重復讀的問題。
三、幻讀(前后讀取數據總量不一致)
事務A在執行讀取操作,需要兩次統計數據的總量,前一次查詢數據總量后,此時事務B執行了新增數據的操作並提交后,這個時候事務A讀取的數據總量和之前統計的不一樣,就像產生了幻覺一樣,平白無故的多了幾條數據,成為幻讀。
舉個例子:
事務A | 事務B | |
---|---|---|
1 | 開始事務 | |
2 | 第一次查詢,數據總量為100條 | |
3 | 開始事務 | |
4 | 其他操作 | |
5 | 增加100條數據 | |
6 | 提交事務 | |
7 | 第二次查詢,數據總量為200條 | |
備注 | 兩次讀取到數據總量不一致 |
不可重復讀和幻讀到底有什么區別呢?
(1)不可重復讀是讀取了其他事務更改的數據,針對insert與update操作
解決:使用行級鎖,鎖定該行,事務A多次讀取操作完成后才釋放該鎖,這個時候才允許其他事務更改剛才的數據。
(2)幻讀是讀取了其他事務新增的數據,針對insert與delete操作
解決:使用表級鎖,鎖定整張表,事務A多次讀取數據總量之后才釋放該鎖,這個時候才允許其他事務新增數據。