(目錄) 概述 筆者在學習資料庫相關內容時,發現關於innoDB在RR級別下究竟能不能保證不發生幻讀這個問題,網上的資料眾說紛紜,筆者在經過總結和自己的試驗之後,在這裡結合自己的理解分析一下這個問題,若有謬誤,歡迎指正。筆者在這裡預設讀者都瞭解了關於幻讀以及innoDB中MVCC和鎖機制的情況,僅對 ...
目錄
概述
筆者在學習資料庫相關內容時,發現關於innoDB在RR級別下究竟能不能保證不發生幻讀這個問題,網上的資料眾說紛紜,筆者在經過總結和自己的試驗之後,在這裡結合自己的理解分析一下這個問題,若有謬誤,歡迎指正。筆者在這裡預設讀者都瞭解了關於幻讀以及innoDB中MVCC和鎖機制的情況,僅對該問題進行分析。
先說結論,innoDB的RR級別下仍然會出現幻讀的情況,但是innoDB還是通過MVCC和鎖儘可能避免幻讀發生。
MVCC做了什麼
首先,關於MVCC,在RR級別下,MVCC只會在事務的首個查詢發生時生成一個Read View,後續的相同查詢都是共用這個Read View,這樣就實現了可重覆讀。同時不難想到,這也解決了幻讀,因為即使另一個事務成功插入並提交,該版本在首次生成的Read View中也是不可見的。但是我們要強調的是,這僅僅是解決了快照讀的幻讀問題。如果事務中只有快照讀,那麼RR級別下不會有幻讀問題。
鎖做了什麼
其次,innoDB通過間隙鎖來儘量避免幻讀問題,這裡簡單舉一個網上常見的例子
CREATE TABLE `test_RR` (
`id` INT NOT NULL,
`teacher_id` INT NULL,
`class_id` INT NULL,
`name` VARCHAR(6) NULL,
PRIMARY KEY (`id`),
INDEX `teacher_id` (`teacher_id` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
transaction A | transaction B | |
---|---|---|
1 | begin; | |
2 | select * from test_RR where teacher_id=1; | |
3 | update test_RR set name='zhang' where teacher_id=1; | |
4 | begin; | |
5 | insert into test_RR values (12,2,6,'zhang'); |
經過測試,這裡的語句5會被阻塞,因為語句3 update是當前讀操作,給表中加了間隙鎖,和B的插入操作互斥。
為啥幻讀:
然而,還有一種情況會導致幻讀
transaction A | transaction B | |
---|---|---|
1 | begin; | |
2 | select * from test_RR where teacher_id=1; | |
3 | begin; | |
4 | insert into test_RR values (12,1,6,'zhang'); | |
5 | commit; | |
6 | update test_RR set name='wang' where teacher_id=1; | |
7 | select * from test_RR where teacher_id=1; | |
8 | commit ; |
語句2結果:
語句7結果:
這裡還是產生了幻讀問題,關鍵在於語句6 update是當前讀,故而將事務B插入提交的行也讀了進來進行了更新,最要命的是讓該行有了一個版本號為當前事務id的版本,這樣語句7查詢的時候自然就將它讀了進來,導致了幻讀問題。
參考資料:
面試被反問,RR級別下能解決幻讀問題那為什麼不叫幻讀級別?_扭秧歌的一隻泱的博客-CSDN博客
Innodb中的事務隔離級別和鎖的關係 - 美團技術團隊 (meituan.com)
幻讀在 InnoDB 中是被如何解決的? - 來份鍋包肉 - 博客園 (cnblogs.com)