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

Mysql數(shù)據(jù)庫幻讀問題舉例詳解

 更新時(shí)間:2025年10月11日 11:19:43   作者:倒懸于世  
數(shù)據(jù)庫幻讀是數(shù)據(jù)庫并發(fā)事務(wù)控制中可能發(fā)生的一種現(xiàn)象,它屬于不可重復(fù)讀的一個(gè)特例,但關(guān)注點(diǎn)不同,這篇文章主要介紹了Mysql數(shù)據(jù)庫幻讀問題的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

我們來詳細(xì)地聊一聊 MySQL InnoDB 中的“幻讀”(Phantom Read)問題。這是一個(gè)在數(shù)據(jù)庫事務(wù)隔離中非常核心且有時(shí)令人困惑的概念。

我會(huì)從定義、例子、原因以及解決方案幾個(gè)方面來徹底講清楚。

1. 什么是幻讀?

官方定義:幻讀指的是在一個(gè)事務(wù)內(nèi),相同的查詢在不同時(shí)間執(zhí)行,返回了不同數(shù)量的行。

這聽起來和“不可重復(fù)讀”很像,但它們有關(guān)鍵區(qū)別:

  • 不可重復(fù)讀 (Non-Repeatable Read):側(cè)重于同一行的數(shù)據(jù)內(nèi)容被修改或刪除。(例如:你第二次查詢時(shí),某行的薪水從10000變成了12000)。

  • 幻讀 (Phantom Read):側(cè)重于新的行被插入(或刪除),導(dǎo)致結(jié)果集的行數(shù)發(fā)生了變化。(針對(duì)結(jié)果集的數(shù)量變化,例如:你第一次查詢有10條記錄,第二次查詢卻冒出了11條)。

簡(jiǎn)單比喻

  • 不可重復(fù)讀:你碗里的一塊紅燒肉被別人咬了一口(數(shù)據(jù)內(nèi)容變了)。

  • 幻讀:你正準(zhǔn)備夾碗里最后一塊紅燒肉時(shí),別人突然又往碗里加了一塊新的肉(數(shù)據(jù)行數(shù)變了)。

2. 幻讀發(fā)生的場(chǎng)景與例子

幻讀發(fā)生的根本原因是:在“可重復(fù)讀(REPEATABLE READ)”及以下隔離級(jí)別中,普通的一致性讀(快照讀)無法阻止其他事務(wù)插入新的、滿足當(dāng)前查詢條件的數(shù)據(jù)。

我們來看一個(gè)經(jīng)典的例子。

表結(jié)構(gòu)

CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `salary` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_salary` (`salary`)
) ENGINE=InnoDB;

INSERT INTO `employee` (`name`, `salary`) VALUES
('Alice', 8000),
('Bob', 9000),
('Charlie', 10000);

時(shí)間線

時(shí)間事務(wù)A (隔離級(jí)別:RR)事務(wù)B
T1START TRANSACTION;
T2SELECT * FROM employee WHERE salary > 8000;
結(jié)果
Bob, 9000
Charlie, 10000
(2 rows)
START TRANSACTION;
T3INSERT INTO employee (name, salary) VALUES ('David', 9500);
COMMIT; <!-- 事務(wù)B提交 -->
T4SELECT * FROM employee WHERE salary > 8000;
結(jié)果
Bob, 9000
Charlie, 10000
(仍然是2 rows!)
(這里沒有幻讀,因?yàn)镽R級(jí)別通過MVCC提供了快照)
T5UPDATE employee SET salary = 8888 WHERE salary > 8000;
(注意:這個(gè)更新操作是當(dāng)前讀,會(huì)看到事務(wù)B已提交的修改)
T6SELECT * FROM employee WHERE salary > 8000;
結(jié)果
Bob, 8888
Charlie, 8888
David, 8888
(3 rows! 幻讀出現(xiàn)了!)

例子分析

  1. T2時(shí)刻:事務(wù)A第一次查詢,得到2條記錄。

  2. T4時(shí)刻:事務(wù)A第二次普通查詢(快照讀)。由于InnoDB的MVCC(多版本并發(fā)控制)機(jī)制,它會(huì)讀取事務(wù)開始時(shí)的數(shù)據(jù)快照,所以看不到事務(wù)B新插入的 David(9500)。此時(shí)還沒有幻讀。

  3. T5時(shí)刻:關(guān)鍵點(diǎn)來了!事務(wù)A執(zhí)行了一個(gè)UPDATE操作。UPDATE/DELETE/INSERT 這類寫操作會(huì)使用“當(dāng)前讀”(Current Read),它會(huì)讀取數(shù)據(jù)庫中最新的、已提交的數(shù)據(jù)。因此,它看到了事務(wù)B插入的 David(9500) 這條記錄,并將其薪水也更新為8888。

  4. T6時(shí)刻:事務(wù)A再次查詢。因?yàn)橹暗腢PDATE操作屬于當(dāng)前事務(wù)的修改,所以MVCC規(guī)則允許它看到自己的修改。于是,它神奇地看到了三條記錄!幻讀就在這一刻發(fā)生了

這個(gè)例子展示了InnoDB中幻讀最典型的特征:即使是在默認(rèn)的RR隔離級(jí)別下,先快照讀,再當(dāng)前讀進(jìn)行寫操作,可能會(huì)意外地影響新插入的行,從而導(dǎo)致數(shù)據(jù)不一致。

3. 解決方案:Next-Key Lock 鎖機(jī)制

InnoDB引擎為了解決幻讀問題,在“可重復(fù)讀(REPEATABLE READ)”隔離級(jí)別下就引入了一種叫做 Next-Key Lock 的鎖機(jī)制。它實(shí)際上是 記錄鎖(Record Lock) 和 間隙鎖(Gap Lock) 的結(jié)合。

  • 記錄鎖 (Record Lock):鎖住索引上的某一條具體記錄。

  • 間隙鎖 (Gap Lock):鎖住索引記錄之間的“間隙”,防止在這個(gè)間隙內(nèi)插入新的數(shù)據(jù)。它是一個(gè)左開右開的區(qū)間 (a, b)。

  • 臨鍵鎖 (Next-Key Lock):是記錄鎖 + 間隙鎖的結(jié)合。它鎖住一條記錄和它前面的間隙。它是一個(gè)左開右閉的區(qū)間 (a, b]

如何解決幻讀?

在上面的例子中,如果事務(wù)A在第一次查詢時(shí),就對(duì) salary > 8000 這個(gè)條件加上了鎖,那么事務(wù)B的插入操作就會(huì)被阻塞,從而杜絕幻讀。

讓我們重演時(shí)間線,但這次事務(wù)A加鎖查詢

時(shí)間事務(wù)A (加鎖查詢)事務(wù)B
T1START TRANSACTION;
T2SELECT * FROM employee WHERE salary > 8000 FOR UPDATE;
(FOR UPDATE 會(huì)給查詢結(jié)果加Next-Key Lock)
結(jié)果:2 rows
START TRANSACTION;
T3INSERT INTO employee (name, salary) VALUES ('David', 9500);
(這條語句會(huì)被阻塞,一直等待事務(wù)A釋放鎖?。?/strong>
T4SELECT ... FOR UPDATE; (再次查詢,結(jié)果一致)...(阻塞中)...
T5COMMIT; (提交事務(wù),釋放鎖)...(阻塞結(jié)束)...
T6(此時(shí)事務(wù)B才能成功插入)

發(fā)生了什么?

當(dāng)事務(wù)A執(zhí)行 SELECT ... FOR UPDATE 時(shí),InnoDB會(huì)為其加Next-Key Lock。假設(shè) salary 上有二級(jí)索引 idx_salary,它可能會(huì)鎖住以下區(qū)間:

  • 鎖住 (8000, 9000] 這個(gè)Next-Key Lock(鎖住9000這條記錄和它前面的間隙)。

  • 鎖住 (9000, 10000] 這個(gè)Next-Key Lock(鎖住10000這條記錄和它前面的間隙)。

  • 鎖住 (10000, +∞] 這個(gè)Next-Key Lock(鎖住正無窮的上界)。

事務(wù)B試圖插入 salary = 9500 的記錄,這個(gè)值落在被事務(wù)A鎖住的 (9000, 10000] 間隙內(nèi),因此插入操作會(huì)被阻塞,直到事務(wù)A提交釋放鎖。這樣就徹底防止了幻讀的發(fā)生。

總結(jié)與最佳實(shí)踐

特性說明
幻讀本質(zhì)同一事務(wù)內(nèi),兩次查詢結(jié)果集行數(shù)不一致, due to 其他事務(wù)的插入刪除操作。
InnoDB的默認(rèn)防御REPEATABLE READ隔離級(jí)別下,InnoDB通過 Next-Key Lock 機(jī)制來防止幻讀。
何時(shí)會(huì)發(fā)生幻讀即使是在RR級(jí)別下,如果你只是進(jìn)行普通的快照讀(SELECT),然后基于此進(jìn)行當(dāng)前讀的寫操作(UPDATE/INSERT/DELETE),仍然可能遇到幻讀。快照讀不加鎖是根源。
徹底解決方法在需要絕對(duì)保證數(shù)據(jù)一致性的關(guān)鍵操作中,使用 加鎖讀
1. SELECT ... FOR UPDATE; (加寫鎖,阻塞其他事務(wù)的寫和加鎖讀)
2. SELECT ... LOCK IN SHARE MODE; (加讀鎖,阻塞其他事務(wù)的寫)
這些語句會(huì)在符合條件的索引上加Next-Key Lock,從而阻止其他事務(wù)在鎖定區(qū)間內(nèi)插入新數(shù)據(jù)。
終極方案將事務(wù)隔離級(jí)別提升至 SERIALIZABLE。在這個(gè)級(jí)別下,所有的讀操作都會(huì)默認(rèn)加上類似 LOCK IN SHARE MODE 的鎖,幻讀自然不會(huì)發(fā)生,但這是以犧牲并發(fā)性能為代價(jià)的,一般不建議使用。

核心要點(diǎn):MySQL InnoDB 在 RR 級(jí)別下已經(jīng)通過 Next-Key Lock 很大程度上解決了幻讀問題。但你需要清楚地知道,只有在你的查詢語句確實(shí)需要加鎖(例如使用了 FOR UPDATE)或者涉及寫操作時(shí),Next-Key Lock 才會(huì)生效。單純的快照讀是無法完全避免幻讀的潛在影響的。

到此這篇關(guān)于Mysql數(shù)據(jù)庫幻讀問題的文章就介紹到這了,更多相關(guān)Mysql幻讀詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mysql計(jì)算2個(gè)日期的月份差和天數(shù)差實(shí)現(xiàn)方式

    mysql計(jì)算2個(gè)日期的月份差和天數(shù)差實(shí)現(xiàn)方式

    文章主要講述如何計(jì)算兩個(gè)日期之間的相隔月份和天數(shù),強(qiáng)調(diào)了日期格式的重要性,并提供了一個(gè)計(jì)算天數(shù)的示例
    2025-11-11
  • DQL數(shù)據(jù)查詢語句使用示例

    DQL數(shù)據(jù)查詢語句使用示例

    DQL(Data?Query?Language?數(shù)據(jù)查詢語言):用于查詢數(shù)據(jù)庫對(duì)象中所包含的數(shù)據(jù)。DQL語言主要的語句:SELECT語句。DQL語言是數(shù)據(jù)庫語言中最核心、最重要的語句,也是使用頻率最高的語句
    2022-12-12
  • mysql字符切割的四種方式匯總

    mysql字符切割的四種方式匯總

    這篇文章主要介紹了mysql字符切割的四種方式匯總,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 詳解一條sql語句在mysql中是如何執(zhí)行的

    詳解一條sql語句在mysql中是如何執(zhí)行的

    這篇文章主要介紹了一條sql語句在mysql中是如何執(zhí)行的,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Mac系統(tǒng)下MySql下載MySQL5.7及詳細(xì)安裝圖解

    Mac系統(tǒng)下MySql下載MySQL5.7及詳細(xì)安裝圖解

    這篇文章主要介紹了Mac系統(tǒng)下MySql下載MySQL5.7及詳細(xì)安裝圖解,本文圖文并茂給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2017-11-11
  • mysql如何匹配JSON中的某個(gè)字段

    mysql如何匹配JSON中的某個(gè)字段

    在MySQL數(shù)據(jù)庫中,可以通過JSON_EXTRACT函數(shù)或->>運(yùn)算符查詢JSON類型字段內(nèi)特定鍵的值,例如,使用JSON_EXTRACT(data, '$.name')或data->>'$.name'從data字段中提取name鍵的值,并用于比較得到name為"張三"的記錄
    2024-11-11
  • Jmeter如何向數(shù)據(jù)庫批量插入數(shù)據(jù)

    Jmeter如何向數(shù)據(jù)庫批量插入數(shù)據(jù)

    這篇文章主要介紹了Jmeter如何向數(shù)據(jù)庫批量插入數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • MySQL查詢性能優(yōu)化七種方式索引潛水

    MySQL查詢性能優(yōu)化七種方式索引潛水

    這篇文章主要介紹了MySQL查詢性能優(yōu)化七種方式索引潛水,文章為榮啊主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下
    2022-08-08
  • MySQL5綠色版windows下安裝總結(jié)(推薦)

    MySQL5綠色版windows下安裝總結(jié)(推薦)

    這篇文章主要介紹了MySQL5綠色版windows下安裝總結(jié),需要的朋友可以參考下
    2017-03-03
  • mysql連續(xù)聚合原理與用法實(shí)例分析

    mysql連續(xù)聚合原理與用法實(shí)例分析

    這篇文章主要介紹了mysql連續(xù)聚合原理與用法,結(jié)合實(shí)例形式分析了mysql連續(xù)聚合的原理、功能、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-12-12

最新評(píng)論

进贤县| 五原县| 镇平县| 松原市| 开封市| 资中县| 安塞县| 阿拉尔市| 烟台市| 新化县| 莱阳市| 永安市| 龙井市| 大庆市| 镇沅| 江口县| 佛山市| 江都市| 苏尼特右旗| 唐山市| 云龙县| 武清区| 堆龙德庆县| 循化| 阿勒泰市| 平谷区| 裕民县| 马尔康县| 泸州市| 澄迈县| 任丘市| 天全县| 封开县| 香港 | 清水河县| 太原市| 三江| 襄樊市| 隆尧县| 宜春市| 织金县|