幻讀是指:在一個事務中,讀取到了其他已經(jīng)提交的事務插入的數(shù)據(jù)行。
創(chuàng)新互聯(lián)專注于尼勒克網(wǎng)站建設(shè)服務及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供尼勒克營銷型網(wǎng)站建設(shè),尼勒克網(wǎng)站制作、尼勒克網(wǎng)頁設(shè)計、尼勒克網(wǎng)站官網(wǎng)定制、小程序定制開發(fā)服務,打造尼勒克網(wǎng)絡公司原創(chuàng)品牌,更為您提供尼勒克網(wǎng)站排名全網(wǎng)營銷落地服務。
MySQL在解決臟讀、不可重復的讀時候,使用了MVCC一致性視圖,同時配合行鎖來解決。
至于幻讀的解決方式,MySQL引入了臨鍵鎖,通過間隙鎖可以避免在兩個行之間插入數(shù)據(jù),從而避免了一個事務在讀取的過程中,讀取到其他事務插入的數(shù)據(jù)行。
首先需要明確的就是“幻讀”概念: 隔離級別是可重復讀,在一個事務中前后兩次查詢,查到了其他事務insert進來的數(shù)據(jù)。
強調(diào)的是讀取到了其他事務插入進來的數(shù)據(jù)。
下面來論證一下可重復讀下幻讀的解決方案
先明確一下,for update語法就是當前讀,也就是查詢當前已經(jīng)提交的數(shù)據(jù),并且是帶悲觀鎖的。沒有for update就是快照讀,也就是根據(jù)readView讀取的undolog中的數(shù)據(jù)。
如果按照以上猜想,那么整個執(zhí)行結(jié)果就違背了 可重復讀 的隔離級別了。
那么我們再假設(shè)select * from TABLE where d = 5 for update;這條語句鎖定的是所有被掃描到的數(shù)據(jù)。
這是因為T2階段的update會被阻塞住,畢竟所有被掃描到的記錄都被鎖定了。
按照上述推理過程,很顯然,即使鎖定所有掃描到的數(shù)據(jù)行,也依然存在幻讀的情況。違背了 可重復讀 的隔離級別。
針對這個情況,我們要解決幻讀的問題,那么就要求針對所有被掃描的記錄行以及還不存在的d=5的記錄行都給鎖住。
至此,當前查詢結(jié)果完全滿足 可重復讀 的隔離級別。
通過以上推論,我們可以總結(jié)一下,在可重復讀的隔離級別下,解決幻讀除了需要鎖定所有掃描到的記錄行外,還需要鎖定行之間的間隙,也就是通過間隙鎖來解決幻讀的問題。
3). 幻讀 :
是指當事務不是獨立執(zhí)行時發(fā)生的一種現(xiàn)象,例如第一個事務對一個表中的數(shù)據(jù)進行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。同時,第二個事務也修改這個表中的數(shù)據(jù),這種修改是向表中插入一行新數(shù)據(jù)。那么,以后就會發(fā)生操作第一個事務的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣。例如,一個編輯人員更改作者提交的文檔,但當生產(chǎn)部門將其更改內(nèi)容合并到該文檔的主復本時,發(fā)現(xiàn)作者已將未編輯的新材料添加到該文檔中。如果在編輯人員和生產(chǎn)部門完成對原始文檔的處理之前,任何人都不能將新材料添加到文檔中,則可以避免該問題。
這張圖本人覺得總結(jié)得挺好的,在一般的互聯(lián)網(wǎng)項目中,基本上用的都是Innodb引擎,一般只涉及到的都是行級鎖,但是如果sql語句中不帶索引進行操作,可能會導致鎖表,這是不推薦的,性能非常低,可能會導致全表掃描等,行鎖的具體實現(xiàn)算法有以下幾種mysql特有的鎖:
Record Lock(記錄鎖):單個行記錄的鎖,一般是唯一索引或者主鍵上的加鎖
Gap Lock(間隙鎖):鎖定一個區(qū)間,但是不包括自身,開區(qū)間的鎖,RR級別才會有間隙鎖,間隙鎖的唯一目的是防止區(qū)間數(shù)據(jù)的插入,所以間隙鎖與間隙鎖之間是不會相互阻塞的
Next-key Lock(臨鍵鎖):與間隙鎖的區(qū)別是包括自身,是左開右閉區(qū)間,RR級別才會有
加鎖規(guī)則里面,包含了兩個“原則”、兩個“優(yōu)化”和一個“bug”。
原則 1:加鎖的基本單位是 next-key lock,希望你還記得,next-key lock 是前開后閉區(qū)間。
原則 2:查找過程中訪問到的對象才會加鎖。
優(yōu)化 1:索引上的等值查詢,給唯一索引加鎖的時候,next-key lock 退化為行鎖。
優(yōu)化 2:索引上的等值查詢,向右遍歷時且最后一個值不滿足等值條件的時候,next-key lock 退化為間隙鎖。
一個 bug:唯一索引上的范圍查詢會訪問到不滿足條件的第一個值為止。
舉例來說明上述的原則:
建表
插入數(shù)據(jù):
INSERT INTO t ( id , c , d ) VALUES (0, 0, 0);
INSERT INTO t ( id , c , d ) VALUES (5, 5, 10);
INSERT INTO t ( id , c , d ) VALUES (10, 10, 10);
INSERT INTO t ( id , c , d ) VALUES (15, 15, 15);
INSERT INTO t ( id , c , d ) VALUES (20, 20, 20);
INSERT INTO t ( id , c , d ) VALUES (25, 25, 25);
例子1:鎖表
因為d字段上沒有建索引,所以涉及該字段的查詢加鎖會鎖住整個表
因為d字段上面沒有建立索引,所以事務1執(zhí)行后會導致整個表被鎖,后面所有的操作都會在等待整個表鎖被釋放
例子2:主鍵/唯一索引 記錄鎖
id字段為主鍵,而且事務1查詢命中了唯一的記錄,默認是加Next-key Lock,區(qū)間是(0,5],但是根據(jù)優(yōu)化1,唯一索引/主鍵上的等值查詢,會退化為行鎖,所以只會鎖5這個記錄。
例子3:主鍵/唯一索引上的間隙鎖
由于表 t 中沒有 id=7 的記錄,所以用我們上面提到的加鎖規(guī)則判斷一下的話:根據(jù)原則 1,加鎖單位是 next-key lock,事務1加鎖范圍就是 (5,10];同時根據(jù)優(yōu)化 2,這是一個等值查詢 (id=7),而 id=10 不滿足查詢條件,next-key lock 退化成間隙鎖,因此最終加鎖的范圍是 (5,10),所以事務2會阻塞,事務3執(zhí)行成功。
例子4:普通索引上的間隙鎖
c字段是普通索引,事務1執(zhí)行時默認是對區(qū)間(0,5]加間隙鎖,根據(jù)優(yōu)化2,非唯一索引/主鍵會繼續(xù)向右遍歷,找到10,所以最終的加鎖為(0,5]的Next-Key鎖+(5,10)的間隙鎖,所以事務2阻塞,事務3成功。
例子5:間隙鎖與行鎖
事務1默認的Next-Key鎖區(qū)間是(0,5],根據(jù)優(yōu)化2會向右遍歷,找到不滿足查詢條件的10,退化成間隙鎖,所以事務1的鎖是(0,5]的Next-Key鎖+(5,10)的間隙鎖,這兩個鎖與行鎖是沖突的,而事務2申請的Next-Key鎖是和事務1一樣,但是c=5的行鎖與事務1沖突,所以產(chǎn)生了阻塞,如果改為update t set d=1000 where c=6;因為此時產(chǎn)生的間隙鎖為(5,10),而間隙鎖與間隙鎖是不沖突的,不會產(chǎn)生阻塞
例子6:lock in share mode鎖覆蓋索引
事務1存在覆蓋索引的情況,不會去回表,lock in share mode這種情況下只會鎖c字段索引,而事務2是對主鍵加行鎖,所以兩者不存在沖突。
例子7:主鍵/唯一索引上的范圍查詢
開始執(zhí)行的時候,要找到第一個 id=10 的行,因此本該是 Next-Key Lock(5,10],根據(jù)優(yōu)化 1, 主鍵 id 上的等值條件,退化成行鎖,只加了 id=10 這一行的行鎖。范圍查找就往后繼續(xù)找,找到 id=15 這一行停下來,因此需要加 Next-Key Lock(10,15],所以事務3是沖突的。
例子8:普通索引上的范圍查詢
開始執(zhí)行時,找到第一個滿足條件的行10,加鎖Next-Key Lock(5,10],因為不是唯一索引,所以不會退化,繼續(xù)向后面找,找到15這一行停下來,因此需要加 Next-Key Lock(10,15],因為是范圍查詢,所以鎖不會退化。
快照讀: 通過MVCC實現(xiàn),該技術(shù)不僅可以保證innodb的可重復讀,而且可以防止幻讀,但是他讀取的數(shù)據(jù)雖然是一致的,但是數(shù)據(jù)是歷史數(shù)據(jù)。
簡單的select操作(不包括 select … lock in share mode, select … for update)
當前讀: 要做到保證數(shù)據(jù)是一致的,同時讀取的數(shù)據(jù)是最新的數(shù)據(jù),innodb提供了next-key lock,即gap鎖與行鎖結(jié)合來實現(xiàn)。
select … lock in share mode
select … for update
insert
update
delete
自己理解:
簡單的select是快照讀,快照讀實現(xiàn)可提交讀,可重復讀和幻讀是通過MVCC+ReadView實現(xiàn)的,而當前讀實現(xiàn)這幾種是通過鎖來實現(xiàn)的,為了說明具體原理,下面介紹下MVCC和ReadView概念,所以簡單的select是通過樂觀鎖實現(xiàn)的,當前讀是通過悲觀鎖實現(xiàn)的。
參考文章:
當前標題:怎么避免mysql幻讀 mysql怎么解決幻讀
地址分享:http://www.rwnh.cn/article20/ddcpejo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序、企業(yè)建站、商城網(wǎng)站、服務器托管、網(wǎng)站營銷、
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)