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

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

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

準備工作

為了便于說明問題,這一篇文章,我們就先使用一個小一點兒的表。

 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);

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

下面的語句序列,是怎么加鎖的,加的鎖又是什么時候釋放的呢?

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

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

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

我們知道,InnoDB的默認事務隔離級別是可重復讀,所以本文接下來沒有特殊說明的部分,都是設定在可重復讀隔離級別下。

幻讀是什么?

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

下面先來看一下這個場景(注意:這是我假設的一個場景):

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

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

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

這里,我需要對“幻讀”做一個說明:

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

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

幻讀有什么問題?

首先是語義上的。session A在T1時刻就聲明了,“我要把所有d=5的行鎖住,不準別的事務進行讀寫操作”。而實際上,這個語義被破壞了。

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

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

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

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

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

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

為了說明這個問題,我給session A在T1時刻再加一個更新語句,即:update t set d=100 where d=5。

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

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

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

這樣看,這些數(shù)據(jù)也沒啥問題,但是我們再來看看這時候binlog里面的內(nèi)容。

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

我統(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*/

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

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

到這里,我們再回顧一下,這個數(shù)據(jù)不一致到底是怎么引入的?

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

所以我們認為,上面的設定不合理,要改。

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

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

這樣對于id=0這一行,在數(shù)據(jù)庫里的最終結(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這一行的問題解決了。

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

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

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

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

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

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

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

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

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

也就是說,跟行鎖有沖突關系的是“另外一個行鎖”。

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

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

這里session B并不會被堵住。因為表t里并沒有c=7這個記錄,因此session A加的是間隙鎖(5,10)。而session B也是在這個間隙加的間隙鎖。它們有共同的目標,即:保護這個間隙,不允許插入值。但,它們之間是不沖突的。

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

你可能會問說,這個supremum從哪兒來的呢?

這是因為+∞是開區(qū)間。實現(xiàn)上,InnoDB給每個索引加了一個不存在的最大值supremum,這樣才符合我們前面說的“都是前開后閉區(qū)間”。

間隙鎖和next-key lock的引入,幫我們解決了幻讀的問題,但同時也帶來了一些“困擾”。

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

相關文章

  • MySQL中的鎖類型都有哪些

    MySQL中的鎖類型都有哪些

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

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

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

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

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

    mysql觸發(fā)器實時檢測一條語句進行備份刪除思路詳解

    遇到過這樣一個需求,在一張表里會不時出現(xiàn) “違規(guī)” 字樣的字段,需要在出現(xiàn)這個字段的時候,把整行的數(shù)據(jù)刪掉,針對這個需求我們該如何操作呢,下面跟隨小編看下mysql觸發(fā)器實時檢測一條語句進行備份刪除的解決思路,一起看看吧
    2021-09-09
  • Mysql開啟外網(wǎng)訪問的全過程記錄

    Mysql開啟外網(wǎng)訪問的全過程記錄

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

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

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

    MySQL服務維護筆記

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

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

    這篇文章主要介紹了ssm框架如何調(diào)用mysql存儲過程,首先是建表,創(chuàng)建存儲過程,本文結(jié)合示例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    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ù)庫超詳細教程

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

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

最新評論

石景山区| 肃南| 嘉峪关市| 丹江口市| 陆丰市| 河东区| 苗栗县| 玉树县| 塘沽区| 肃南| 佛学| 堆龙德庆县| 佛坪县| 武山县| 贵溪市| 平邑县| 股票| 乃东县| 达州市| 漳浦县| 阳城县| 华蓥市| 塘沽区| 宁远县| 保亭| 三亚市| 子洲县| 清河县| 梅河口市| 扶风县| 读书| 利津县| 琼结县| 元阳县| 曲麻莱县| 图片| 虎林市| 九台市| 定陶县| 弋阳县| 拉萨市|