問題引出
我之前的一篇博客 數據庫並發不一致分析 有提到過事務隔離級別以及相應加鎖方式、能夠解決的並發問題。
標准情況下,在 RR(Repeatable Read) 隔離級別下能解決不可重復讀(當行修改)的問題,但是不能解決幻讀的問題。
而之前有看過一篇 mysql 加鎖的文章 MySQL 加鎖處理分析,里面有提到一點:
對於Innodb,Repeatable Read (RR) 針對當前讀,RR隔離級別保證對讀取到的記錄加鎖 (記錄鎖),同時保證對讀取的范圍加鎖,新的滿足查詢條件的記錄不能夠插入 (間隙鎖),不存在幻讀現象。
那么問題來了,到底 Innodb 中 RR 隔離級別是否能解決幻讀呢?
在 MySQL 加鎖處理分析這篇文章下面的評論中,有這樣的一個交流:
ontheway
弱弱地問一句,我看的書里面都說的是RR隔離級別不允許臟讀和不可重復讀,但是可以幻讀,怎么和作者說的不一樣呢?hedengcheng(作者)
你說的沒錯,因此我在文章一開始,就強調了這一點。mysql innodb引擎的實現,跟標准有所不同。
求證官方文檔
MySQL Innodb 引擎的實現,跟標准有所不同,針對這個問題,我表示懷疑,於是查看 mysql 官方文檔關於 RR的解釋,里面有這么一段話:
For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition. For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it. For other search conditions, InnoDB locks the index range scanned, using gap locks or next-key locks to block insertions by other sessions into the gaps covered by the range.
大致意思就是,在 RR 級別下,如果查詢條件能使用上唯一索引,或者是一個唯一的查詢條件,那么僅加行鎖,如果是一個范圍查詢,那么就會給這個范圍加上 gap 鎖或者 next-key鎖 (行鎖+gap鎖)。
從這句話的理解來看,和文章里的解釋一樣,由於 RR 級別對於范圍會加 GAP 鎖,這個和 sql 的標准是有一些差異的。
其他解釋
后面又發現了一篇文章 Understanding InnoDB transaction isolation levels,文章中又提到:
This isolation level is the default for InnoDB. Although this isolation level solves the problem of non-repeatable read, but there is another possible problem phantom reads.
大概意思是,RR 能解決不可重復讀的問題,但仍可能發生幻讀,懷疑作者並不了解 Innodb 的特殊實現,評論中也有提到:
Do you mean 'write skew' instead of 'phantom reads'? The 'repeatable read' in SQL standard allows 'phantom reads', however, since InnoDB uses next-key locking this anomaly does not exist in this level. Looks like it's equivalent to 'snapshot isolation' in Postgres and Oracle.
再來看一篇文章 MySQL的InnoDB的幻讀問題,這里面提供了一些例子,還沒來得及分析,但最后的結論是:
MySQL InnoDB的可重復讀並不保證避免幻讀,需要應用使用加鎖讀來保證。而這個加鎖度使用到的機制就是next-key locks。
最終結論
Innodb 的 RR 隔離界別對范圍會加上 GAP,理論上不會存在幻讀,但是是否有例外呢,這個還需要進一步求證。