最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mysql中幻讀的概念以及如何解決

 更新時(shí)間:2023年05月22日 08:49:17   作者:ITWords  
這篇文章主要介紹了Mysql中幻讀的概念以及如何解決,幻讀指的是一個(gè)事務(wù)在前后兩次查詢同一個(gè)范圍的時(shí)候,后一次查詢看到了前一次查詢沒(méi)有看到的行,需要的朋友可以參考下

準(zhǔn)備工作

為了便于說(shuō)明問(wèn)題,這一篇文章,我們就先使用一個(gè)小一點(diǎn)兒的表。

 CREATE TABLE `t` (
   `id` int(11) NOT NULL,
   `c` int(11) DEFAULT NULL,
   `d` int(11) DEFAULT NULL,
   PRIMARY KEY (`id`),
   KEY `c` (`c`)
 ) ENGINE=InnoDB;
 ?
 insert into t values(0,0,0),(5,5,5),
 (10,10,10),(15,15,15),(20,20,20),(25,25,25);

這個(gè)表除了主鍵id外,還有一個(gè)索引c,初始化語(yǔ)句在表中插入了6行數(shù)據(jù)。

下面的語(yǔ)句序列,是怎么加鎖的,加的鎖又是什么時(shí)候釋放的呢?

begin;
 select * from t where d=5 for update;
 commit;

比較好理解的是,這個(gè)語(yǔ)句會(huì)命中d=5的這一行,對(duì)應(yīng)的主鍵id=5,因此在select 語(yǔ)句執(zhí)行完成后,id=5這一行會(huì)加一個(gè)寫鎖,而且由于兩階段鎖協(xié)議,這個(gè)寫鎖會(huì)在執(zhí)行commit語(yǔ)句的時(shí)候釋放。

由于字段d上沒(méi)有索引,因此這條查詢語(yǔ)句會(huì)做全表掃描。那么,其他被掃描到的,但是不滿足條件的5行記錄上,會(huì)不會(huì)被加鎖呢?

我們知道,InnoDB的默認(rèn)事務(wù)隔離級(jí)別是可重復(fù)讀,所以本文接下來(lái)沒(méi)有特殊說(shuō)明的部分,都是設(shè)定在可重復(fù)讀隔離級(jí)別下。

幻讀是什么?

現(xiàn)在,我們就來(lái)分析一下,如果只在id=5這一行加鎖,而其他行的不加鎖的話,會(huì)怎么樣。

下面先來(lái)看一下這個(gè)場(chǎng)景(注意:這是我假設(shè)的一個(gè)場(chǎng)景):

可以看到,session A里執(zhí)行了三次查詢,分別是Q1、Q2和Q3。它們的SQL語(yǔ)句相同,都是select * from t where d=5 for update。這個(gè)語(yǔ)句的意思你應(yīng)該很清楚了,查所有d=5的行,而且使用的是當(dāng)前讀,并且加上寫鎖?,F(xiàn)在,我們來(lái)看一下這三條SQL語(yǔ)句,分別會(huì)返回什么結(jié)果。

  1. Q1只返回id=5這一行;
  2. 在T2時(shí)刻,session B把id=0這一行的d值改成了5,因此T3時(shí)刻Q2查出來(lái)的是id=0和id=5這兩行;
  3. 在T4時(shí)刻,session C又插入一行(1,1,5),因此T5時(shí)刻Q3查出來(lái)的是id=0、id=1和id=5的這三行。

其中,Q3讀到id=1這一行的現(xiàn)象,被稱為“幻讀”。也就是說(shuō),幻讀指的是一個(gè)事務(wù)在前后兩次查詢同一個(gè)范圍的時(shí)候,后一次查詢看到了前一次查詢沒(méi)有看到的行。

這里,我需要對(duì)“幻讀”做一個(gè)說(shuō)明:

  1. 在可重復(fù)讀隔離級(jí)別下,普通的查詢是快照讀,是不會(huì)看到別的事務(wù)插入的數(shù)據(jù)的。因此,幻讀在“當(dāng)前讀”下才會(huì)出現(xiàn)。
  2. 上面session B的修改結(jié)果,被session A之后的select語(yǔ)句用“當(dāng)前讀”看到,不能稱為幻讀?;米x僅專指“新插入的行”。

因?yàn)檫@三個(gè)查詢都是加了for update,都是當(dāng)前讀。而當(dāng)前讀的規(guī)則,就是要能讀到所有已經(jīng)提交的記錄的最新值。并且,session B和sessionC的兩條語(yǔ)句,執(zhí)行后就會(huì)提交,所以Q2和Q3就是應(yīng)該看到這兩個(gè)事務(wù)的操作效果,而且也看到了,這跟事務(wù)的可見(jiàn)性規(guī)則并不矛盾。

幻讀有什么問(wèn)題?

首先是語(yǔ)義上的。session A在T1時(shí)刻就聲明了,“我要把所有d=5的行鎖住,不準(zhǔn)別的事務(wù)進(jìn)行讀寫操作”。而實(shí)際上,這個(gè)語(yǔ)義被破壞了。

如果現(xiàn)在這樣看感覺(jué)還不明顯的話,我再往session B和session C里面分別加一條SQL語(yǔ)句,你再看看會(huì)出現(xiàn)什么現(xiàn)象。

session B的第二條語(yǔ)句update t set c=5 where id=0,語(yǔ)義是“我把id=0、d=5這一行的c值,改成了5”。

由于在T1時(shí)刻,session A 還只是給id=5這一行加了行鎖, 并沒(méi)有給id=0這行加上鎖。因此,session B在T2時(shí)刻,是可以執(zhí)行這兩條update語(yǔ)句的。這樣,就破壞了 session A 里Q1語(yǔ)句要鎖住所有d=5的行的加鎖聲明。

session C也是一樣的道理,對(duì)id=1這一行的修改,也是破壞了Q1的加鎖聲明。

數(shù)據(jù)一致性問(wèn)題

我們知道,鎖的設(shè)計(jì)是為了保證數(shù)據(jù)的一致性。而這個(gè)一致性,不止是數(shù)據(jù)庫(kù)內(nèi)部數(shù)據(jù)狀態(tài)在此刻的一致性,還包含了數(shù)據(jù)和日志在邏輯上的一致性。

為了說(shuō)明這個(gè)問(wèn)題,我給session A在T1時(shí)刻再加一個(gè)更新語(yǔ)句,即:update t set d=100 where d=5。

update的加鎖語(yǔ)義和select …for update 是一致的,所以這時(shí)候加上這條update語(yǔ)句也很合理。session A聲明說(shuō)“要給d=5的語(yǔ)句加上鎖”,就是為了要更新數(shù)據(jù),新加的這條update語(yǔ)句就是把它認(rèn)為加上了鎖的這一行的d值修改成了100。

現(xiàn)在,我們來(lái)分析一下圖3執(zhí)行完成后,數(shù)據(jù)庫(kù)里會(huì)是什么結(jié)果。

  1. 經(jīng)過(guò)T1時(shí)刻,id=5這一行變成 (5,5,100),當(dāng)然這個(gè)結(jié)果最終是在T6時(shí)刻正式提交的;
  2. 經(jīng)過(guò)T2時(shí)刻,id=0這一行變成(0,5,5);
  3. 經(jīng)過(guò)T4時(shí)刻,表里面多了一行(1,5,5);
  4. 其他行跟這個(gè)執(zhí)行序列無(wú)關(guān),保持不變。

這樣看,這些數(shù)據(jù)也沒(méi)啥問(wèn)題,但是我們?cè)賮?lái)看看這時(shí)候binlog里面的內(nèi)容。

  1. T2時(shí)刻,session B事務(wù)提交,寫入了兩條語(yǔ)句;
  2. T4時(shí)刻,session C事務(wù)提交,寫入了兩條語(yǔ)句;
  3. T6時(shí)刻,session A事務(wù)提交,寫入了update t set d=100 where d=5 這條語(yǔ)句。

我統(tǒng)一放到一起的話,就是這樣的:

 update t set d=5 where id=0; /*(0,0,5)*/
 update t set c=5 where id=0; /*(0,5,5)*/
 ?
 insert into t values(1,1,5); /*(1,1,5)*/
 update t set c=5 where id=1; /*(1,5,5)*/
 ?
 update t set d=100 where d=5;/*所有d=5的行,d改成100*/

好,你應(yīng)該看出問(wèn)題了。這個(gè)語(yǔ)句序列,不論是拿到備庫(kù)去執(zhí)行,還是以后用binlog來(lái)克隆一個(gè)庫(kù),這三行的結(jié)果,都變成了 (0,5,100)、(1,5,100)和(5,5,100)。

也就是說(shuō),id=0和id=1這兩行,發(fā)生了數(shù)據(jù)不一致。這個(gè)問(wèn)題很嚴(yán)重,是不行的。

到這里,我們?cè)倩仡櫼幌拢?strong>這個(gè)數(shù)據(jù)不一致到底是怎么引入的?

我們分析一下可以知道,這是我們假設(shè)“select * from t where d=5 for update這條語(yǔ)句只給d=5這一行,也就是id=5的這一行加鎖”導(dǎo)致的。

所以我們認(rèn)為,上面的設(shè)定不合理,要改。

那怎么改呢?我們把掃描過(guò)程中碰到的行,也都加上寫鎖,再來(lái)看看執(zhí)行效果。

由于session A把所有的行都加了寫鎖,所以session B在執(zhí)行第一個(gè)update語(yǔ)句的時(shí)候就被鎖住了。需要等到T6時(shí)刻session A提交以后,session B才能繼續(xù)執(zhí)行。

這樣對(duì)于id=0這一行,在數(shù)據(jù)庫(kù)里的最終結(jié)果還是 (0,5,5)。在binlog里面,執(zhí)行序列是這樣的:

 insert into t values(1,1,5); /*(1,1,5)*/
 update t set c=5 where id=1; /*(1,5,5)*/
 ?
 update t set d=100 where d=5;/*所有d=5的行,d改成100*/
 ?
 update t set d=5 where id=0; /*(0,0,5)*/
 update t set c=5 where id=0; /*(0,5,5)*/

可以看到,按照日志順序執(zhí)行,id=0這一行的最終結(jié)果也是(0,5,5)。所以,id=0這一行的問(wèn)題解決了。

但同時(shí)你也可以看到,id=1這一行,在數(shù)據(jù)庫(kù)里面的結(jié)果是(1,5,5),而根據(jù)binlog的執(zhí)行結(jié)果是(1,5,100),也就是說(shuō)幻讀的問(wèn)題還是沒(méi)有解決。為什么我們已經(jīng)這么“兇殘”地,把所有的記錄都上了鎖,還是阻止不了id=1這一行的插入和更新呢?

原因很簡(jiǎn)單。在T3時(shí)刻,我們給所有行加鎖的時(shí)候,id=1這一行還不存在,不存在也就加不上鎖。

也就是說(shuō),即使把所有的記錄都加上鎖,還是阻止不了新插入的記錄,這也是為什么“幻讀”會(huì)被單獨(dú)拿出來(lái)解決的原因。

現(xiàn)在你知道了,產(chǎn)生幻讀的原因是,行鎖只能鎖住行,但是新插入記錄這個(gè)動(dòng)作,要更新的是記錄之間的“間隙”。因此,為了解決幻讀問(wèn)題,InnoDB只好引入新的鎖,也就是間隙鎖(Gap Lock)。

顧名思義,間隙鎖,鎖的就是兩個(gè)值之間的空隙。比如文章開(kāi)頭的表t,初始化插入了6個(gè)記錄,這就產(chǎn)生了7個(gè)間隙。

這樣,當(dāng)你執(zhí)行 select * from t where d=5 for update的時(shí)候,就不止是給數(shù)據(jù)庫(kù)中已有的6個(gè)記錄加上了行鎖,還同時(shí)加了7個(gè)間隙鎖。這樣就確保了無(wú)法再插入新的記錄。

也就是說(shuō)這時(shí)候,在一行行掃描的過(guò)程中,不僅將給行加上了行鎖,還給行兩邊的空隙,也加上了間隙鎖。

現(xiàn)在你知道了,數(shù)據(jù)行是可以加上鎖的實(shí)體,數(shù)據(jù)行之間的間隙,也是可以加上鎖的實(shí)體。但是間隙鎖跟我們之前碰到過(guò)的鎖都不太一樣。

比如行鎖,分成讀鎖和寫鎖。下圖就是這兩種類型行鎖的沖突關(guān)系。

也就是說(shuō),跟行鎖有沖突關(guān)系的是“另外一個(gè)行鎖”。

但是間隙鎖不一樣,跟間隙鎖存在沖突關(guān)系的,是“往這個(gè)間隙中插入一個(gè)記錄”這個(gè)操作。間隙鎖之間都不存在沖突關(guān)系。

這句話不太好理解,我給你舉個(gè)例子:

這里session B并不會(huì)被堵住。因?yàn)楸韙里并沒(méi)有c=7這個(gè)記錄,因此session A加的是間隙鎖(5,10)。而session B也是在這個(gè)間隙加的間隙鎖。它們有共同的目標(biāo),即:保護(hù)這個(gè)間隙,不允許插入值。但,它們之間是不沖突的。

間隙鎖和行鎖合稱next-key lock,每個(gè)next-key lock是前開(kāi)后閉區(qū)間。也就是說(shuō),我們的表t初始化以后,如果用select * from t for update要把整個(gè)表所有記錄鎖起來(lái),就形成了7個(gè)next-key lock,分別是 (-∞,0]、(0,5]、(5,10]、(10,15]、(15,20]、(20, 25]、(25, +supremum]。

你可能會(huì)問(wèn)說(shuō),這個(gè)supremum從哪兒來(lái)的呢?

這是因?yàn)?∞是開(kāi)區(qū)間。實(shí)現(xiàn)上,InnoDB給每個(gè)索引加了一個(gè)不存在的最大值supremum,這樣才符合我們前面說(shuō)的“都是前開(kāi)后閉區(qū)間”。

間隙鎖和next-key lock的引入,幫我們解決了幻讀的問(wèn)題,但同時(shí)也帶來(lái)了一些“困擾”。

到此這篇關(guān)于Mysql中幻讀的概念以及如何解決的文章就介紹到這了,更多相關(guān)Mysql 幻讀概念內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySQL中的鎖類型都有哪些

    MySQL中的鎖類型都有哪些

    這篇文章主要介紹了MySQL中的鎖類型都有哪些,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • MySQL高可用架構(gòu)之MHA架構(gòu)全解

    MySQL高可用架構(gòu)之MHA架構(gòu)全解

    MHA(Master HA)是一款開(kāi)源的 MySQL 的高可用程序,它為 MySQL 主從復(fù)制架構(gòu)提供了 automating master failover 功能,本文重點(diǎn)給大家介紹MySQL高可用架構(gòu)之MHA架構(gòu)全解,感興趣的朋友一起看看吧
    2021-07-07
  • Mysql的Explain使用方式及索引總結(jié)

    Mysql的Explain使用方式及索引總結(jié)

    這篇文章主要介紹了Mysql的Explain使用方式及索引總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • mysql觸發(fā)器實(shí)時(shí)檢測(cè)一條語(yǔ)句進(jìn)行備份刪除思路詳解

    mysql觸發(fā)器實(shí)時(shí)檢測(cè)一條語(yǔ)句進(jìn)行備份刪除思路詳解

    遇到過(guò)這樣一個(gè)需求,在一張表里會(huì)不時(shí)出現(xiàn) “違規(guī)” 字樣的字段,需要在出現(xiàn)這個(gè)字段的時(shí)候,把整行的數(shù)據(jù)刪掉,針對(duì)這個(gè)需求我們?cè)撊绾尾僮髂兀旅娓S小編看下mysql觸發(fā)器實(shí)時(shí)檢測(cè)一條語(yǔ)句進(jìn)行備份刪除的解決思路,一起看看吧
    2021-09-09
  • Mysql開(kāi)啟外網(wǎng)訪問(wèn)的全過(guò)程記錄

    Mysql開(kāi)啟外網(wǎng)訪問(wèn)的全過(guò)程記錄

    mysql 默認(rèn)是不允許外放訪問(wèn)的,只允許 localhost 或 127.0.0.1 訪問(wèn),下面這篇文章主要給大家介紹了關(guān)于Mysql開(kāi)啟外網(wǎng)訪問(wèn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • MySQL中實(shí)現(xiàn)高性能高并發(fā)計(jì)數(shù)器方案(例如文章點(diǎn)擊數(shù))

    MySQL中實(shí)現(xiàn)高性能高并發(fā)計(jì)數(shù)器方案(例如文章點(diǎn)擊數(shù))

    這篇文章主要介紹了MySQL中實(shí)現(xiàn)高性能高并發(fā)計(jì)數(shù)器方案,本文中的計(jì)數(shù)器是指如文章的點(diǎn)擊數(shù)、喜歡數(shù)、瀏覽次數(shù)等,需要的朋友可以參考下
    2014-10-10
  • MySQL服務(wù)維護(hù)筆記

    MySQL服務(wù)維護(hù)筆記

    MySQL服務(wù)維護(hù)筆記...
    2006-12-12
  • ssm框架如何調(diào)用mysql存儲(chǔ)過(guò)程

    ssm框架如何調(diào)用mysql存儲(chǔ)過(guò)程

    這篇文章主要介紹了ssm框架如何調(diào)用mysql存儲(chǔ)過(guò)程,首先是建表,創(chuàng)建存儲(chǔ)過(guò)程,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • MySQL用B+樹作為索引結(jié)構(gòu)有什么好處

    MySQL用B+樹作為索引結(jié)構(gòu)有什么好處

    這篇文章主要介紹了MySQL用B+樹作為索引結(jié)構(gòu)有什么好處,幫助大家更好的理解和使用MySQL 索引,感興趣的朋友可以了解下
    2021-01-01
  • 在idea中使用mysql數(shù)據(jù)庫(kù)超詳細(xì)教程

    在idea中使用mysql數(shù)據(jù)庫(kù)超詳細(xì)教程

    這篇文章主要介紹了如何在IntelliJ IDEA中連接MySQL數(shù)據(jù)庫(kù),并使用控制臺(tái)執(zhí)行SQL語(yǔ)句,還詳細(xì)講解了如何使用MyBatis Generator快速生成實(shí)體類、接口和SQL文件,以及如何解決Lombok依賴問(wèn)題,需要的朋友可以參考下
    2025-02-02

最新評(píng)論

贡觉县| 东明县| 潜山县| 元朗区| 崇文区| 分宜县| 莱西市| 崇义县| 迭部县| 普兰县| 沅江市| 张北县| 博罗县| 南召县| 嘉荫县| 柞水县| 大新县| 洛隆县| 景东| 静海县| 巴东县| 马龙县| 巍山| 潜江市| 蒙自县| 雷山县| 龙江县| 高清| 墨玉县| 新田县| 扬州市| 清徐县| 本溪| 甘洛县| 余江县| 绥德县| 长春市| 瑞丽市| 湖南省| 万安县| 潜江市|