聲明
由於貼mysql截圖會佔太大篇幅,影響整體閱讀理解,所以沒有貼mysql截圖,但是下面步驟都是親自試驗過的
四種隔離級別(由低到高)
- Read Uncommitted讀未提交:可以看到其他事務未提交的內容
- Read Committed讀已提交:可以看到其他事務已提交讀內容
- Repeatable Read可重複度:事務開始時和事務結束時讀到的數據完全相同
- Serializable串行:事務必須逐步執行,後來的會排隊
隔離級別下的問題
前提條件:同時開啓A和B兩個事務
髒讀:
- 事務查詢id爲1的數據,num字段爲1
- B事務將id爲1的數據num更新爲2,但未commit
- 事務查詢id爲1的數據,num字段變爲2
- B回滾,則A讀到的數據爲髒數據
不可重複讀:
- 事務查詢id爲1的數據,num字段爲1
- B事務將id爲1的數據num更新爲2,commit
- 事務查詢id爲1的數據,num字段變爲2
- A事務前後兩次讀到的同一條記錄num字段不同,不可重複讀
幻讀:
- A事務查詢id >=1 and id <=3的數據,得到id=1和id=3兩條數據(注意:沒有id=2的數據)
- B事務插入id=2的數據
- A事務再查詢id >=1 and id <=3的數據,發現多了一條id=2的數據,即兩次查詢數據的行數不同
各種隔離模式下會出現的問題
- 讀未提交:髒讀、不可重複度、幻讀
- 讀已提交:不可重複讀、幻讀
- 可重複讀:幻讀
間隙鎖(gap-key)
在一個事務中select for update查詢某條記錄,會鎖定該條記錄的前後空行,什麼叫空行呢,看個例子就知道了
id | num |
1 | 2 |
2 | 4 |
3 | 6 |
4 | 7 |
執行select * from table where num = 4 for update
insert into table ('', 3)和insert into table ('', 5)都會被阻塞,即4前後的空行都無法插入
next-key
由於gap-key只能鎖定記錄之間的間隙,但是我們上面查詢num=4的行也不能被更改,索引該行也會被加行鎖,此時這種既加行速鎖,又加間隙鎖,我們稱之爲next-key
MVCC
- 事務A:查詢num>=2 and num<=4的記錄,但是不加for update!不加for update!不加for update!
- 事務B:insert into table ('', 3),不會被阻塞,不會被阻塞,不會被阻塞!然後commit
- 事務A:查詢num>=2 and num<=4的記錄,和之前查詢相同
- 事務A:提交
- 事務A:查詢num>=2 and num<=4的記錄,可以查詢到num=3的記錄,此時返回3條結果
原理:假如事務A在開啓的時候版本號爲2,當更改或者插入數據後,該條記錄的版本號+1,也就是說事務B插入num=3的記錄的版本號爲3,事務A在提交前的所有select都只能查詢到版本號<=2的記錄,也就是說不會產生上面說的幻讀。
MVCC和next-key
看到這裏可能有小夥伴有點蒙了,一會MVCC一會next-key,到底用什麼結局幻讀的???
其實,innodb採用next-key + MVCC去解決幻讀問題的:
- 在查詢加for update時,會用next-key解決幻讀問題,新的insert和update會阻塞
- 在查詢不加for update時,會用MVCC解決幻讀問題,新的insert和update不會阻塞