今天面試被問到了這個問題故爵,由于自己還沒有系統(tǒng)的學(xué)習(xí)過mysql玻粪,遇到問題總會一知半解,因此對這個問題的理解不是很清晰诬垂,今天通過讀文章和跟同事討論劲室,先得出個一般的結(jié)論。后續(xù)還是的踏實的看看書啊结窘。
另外面試官提到開啟一個參數(shù)能夠解決幻讀的問題很洋,雖然沒有給答案,我猜測應(yīng)該他說的是這個innodb_locks_unsafe_for_binlog隧枫。
參考文章里面有比較經(jīng)典的美團技術(shù)博客的文章喉磁,書籍、文章也得彻倥В看常新协怒,要細細琢磨。
參考文章:
什么是幻讀卑笨?
如果使用鎖機制來實現(xiàn)這兩種隔離級別孕暇,在可重復(fù)讀中,該sql第一次讀取到數(shù)據(jù)后湾趾,就將這些數(shù)據(jù)加鎖芭商,其它事務(wù)無法修改這些數(shù)據(jù),就可以實現(xiàn)可重復(fù)讀了搀缠。但這種方法卻無法鎖住insert的數(shù)據(jù),所以當事務(wù)A先前讀取了數(shù)據(jù)近迁,或者修改了全部數(shù)據(jù)艺普,事務(wù)B還是可以insert數(shù)據(jù)提交,這時事務(wù)A就會發(fā)現(xiàn)莫名其妙多了一條之前沒有的數(shù)據(jù),這就是幻讀歧譬,不能通過行鎖來避免
解決了不重復(fù)讀岸浑,保證了同一個事務(wù)里,查詢的結(jié)果都是事務(wù)開始時的狀態(tài)(一致性)瑰步。但是矢洲,如果另一個事務(wù)同時提交了新數(shù)據(jù),本事務(wù)再更新時缩焦,就會“驚奇的”發(fā)現(xiàn)了這些新數(shù)據(jù)读虏,貌似之前讀到的數(shù)據(jù)是“鬼影”一樣的幻覺。
我理解:只限于數(shù)據(jù)庫中關(guān)于事務(wù)隔離級別定義袁滥,由于事務(wù)鎖不了插入操作盖桥,后續(xù)查詢或者更新的時候像出現(xiàn)了幻覺一樣,無形中多了別的事務(wù)已經(jīng)commit insert的一條記錄题翻。
innodb rr模式下揩徊,避免了嗎?先說自己的結(jié)論:
mysql innodb RR隔離級別下嵌赠,也不能完全防止幻讀問題的發(fā)生塑荒。如果防止幻讀,在涉及到業(yè)務(wù)需要防止幻讀的時候姜挺,必須開啟鎖(不管是共享鎖還是排他鎖)齿税。----你真的有這個應(yīng)用場景的話
小問題補充
- 1 首先是innodb_locks_unsafe_for_binlog這個參數(shù)召耘,代表是否禁用間隙鎖痊乾,默認是off的彼念,即開啟間隙鎖谆棱。
- 2 mysql innodb rr級別情況下趁桃,并且innodb_locks_unsafe_for_binlog參數(shù)是關(guān)閉的雁歌,能夠解決幻讀的場景呻引。
具體可以去看參考文章1和2颜及,里面對解決的問題做了細致的分析掖肋,這里給出結(jié)論如下:- 2.1 mysql提出的當前讀和快照讀的概念仆葡。
- 快照讀:就是普通的select
select * from table ….;- 當前讀:特殊的讀操作,插入/更新/刪除操作志笼,屬于當前讀沿盅,處理的都是當前的數(shù)據(jù),需要加鎖纫溃。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert;
update ;
delete;
- 2.2 對于快照讀的場景腰涧,通過mvcc版本管理來解決幻讀的問題。就是a事務(wù)只做了兩次查詢操作紊浩,兩次查詢中間即使有符合條件的插入窖铡,第二次查詢的結(jié)果也是原來的數(shù)據(jù)信息疗锐。
mvcc版本管理的具體意思如下(有的文章說底層是通過redo和undo日志搞定的,不糾結(jié)费彼,以后看滑臊。):
在InnoDB中,會在每行數(shù)據(jù)后添加兩個額外的隱藏的值來實現(xiàn)MVCC箍铲,這兩個值一個記錄這行數(shù)據(jù)何時被創(chuàng)建雇卷,另外一個記錄這行數(shù)據(jù)何時過期(或者被刪除)。 在實際操作中颠猴,存儲的并不是時間关划,而是事務(wù)的版本號,每開啟一個新事務(wù)芙粱,事務(wù)的版本號就會遞增祭玉。 在可重讀Repeatable reads事務(wù)隔離級別下:
- SELECT時,讀取創(chuàng)建版本號<=當前事務(wù)版本號春畔,刪除版本號為空或>當前事務(wù)版本號脱货。
- INSERT時,保存當前事務(wù)版本號為行的創(chuàng)建版本號
- DELETE時律姨,保存當前事務(wù)版本號為行的刪除版本號
- UPDATE時振峻,插入一條新紀錄,保存當前事務(wù)版本號為行創(chuàng)建版本號择份,同時保存當前事務(wù)版本號到原來刪除的行
這里面感覺有個前提是a事務(wù)的版本號比那個插入事務(wù)的版本號要小扣孟。
2.3 針對當前讀的情況,mysql是通過Next-Key鎖搞定的荣赶。就是再當前讀的情況下凤价,會加入一個范圍鎖,鎖住一個區(qū)間拔创,區(qū)間內(nèi)如果有別的事務(wù)進行插入操作利诺,是要等待當前事務(wù)提交的。
-
2.4 搞不定的場景剩燥。假設(shè)事務(wù)a先進行了一次范圍查詢操作慢逾,由于是普通的查詢,并沒有開始間隙鎖灭红,也就說允許其他事務(wù)在查詢的范圍內(nèi)進行插入的侣滩,假設(shè)這個時候其他事務(wù)插入并提交,那么a事務(wù)如果進行更新操作变擒,那就會把插入的數(shù)據(jù)也更新了君珠。
我感覺哈,其實2.4經(jīng)常是我們寫代碼里面面臨的場景娇斑,我們一般寫代碼的時候經(jīng)常會select一堆數(shù)據(jù)葛躏,完了內(nèi)存里面判斷澈段,完了去更新數(shù)據(jù)悠菜。如果以后寫的過程中遇到這種場景的話舰攒,一定要注意了。
2.5 其實真的寫代碼的時候悔醋,鎖的范圍一定要小要小摩窃,盡量別搞gap鎖。
2.6 待確認的問題:MVCC機制與redo芬骄、undo日志的關(guān)系猾愿;gap間隙鎖怎么加區(qū)間的?
以上都沒有經(jīng)過實踐驗證账阻,純理論推導(dǎo)蒂秘。