欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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釋放鎖!)
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如何定時(shí)自動(dòng)新增分區(qū)

    mysql如何定時(shí)自動(dòng)新增分區(qū)

    這篇文章主要介紹了mysql如何定時(shí)自動(dòng)新增分區(qū)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • 詳解Windows?Server?2012下安裝MYSQL5.7.24的問題

    詳解Windows?Server?2012下安裝MYSQL5.7.24的問題

    這篇文章主要介紹了Windows?Server?2012下安裝MYSQL5.7.24的詳細(xì)過程,本文通過圖文并茂實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • mybatis分頁插件pageHelper詳解及簡(jiǎn)單實(shí)例

    mybatis分頁插件pageHelper詳解及簡(jiǎn)單實(shí)例

    這篇文章主要介紹了mybatis分頁插件pageHelper詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • SQL優(yōu)化教程之in與range查詢

    SQL優(yōu)化教程之in與range查詢

    這篇文章主要介紹了給大家介紹了SQL優(yōu)化之in與range查詢的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 如何設(shè)置才能遠(yuǎn)程登錄Mysql數(shù)據(jù)庫

    如何設(shè)置才能遠(yuǎn)程登錄Mysql數(shù)據(jù)庫

    本地機(jī)器安裝的數(shù)據(jù)庫,本地程序可以訪問,但是同事的機(jī)器卻無法連接訪問,發(fā)現(xiàn)是mysql數(shù)據(jù)庫沒有開啟遠(yuǎn)程訪問。那么我們需要如何設(shè)置呢,這就是本文探討的內(nèi)容了
    2014-08-08
  • MySQL 內(nèi)存表和臨時(shí)表的用法詳解

    MySQL 內(nèi)存表和臨時(shí)表的用法詳解

    這篇文章主要介紹了MySQL 內(nèi)存表和臨時(shí)表的用法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • 解決Mysql多行子查詢的使用及空值問題

    解決Mysql多行子查詢的使用及空值問題

    所謂多行子查詢,是指執(zhí)行查詢語句獲得的結(jié)果集中返回了多行數(shù)據(jù)的子查詢,今天通過本文給大家分享Mysql多行子查詢的使用及空值問題,感興趣的朋友一起看看吧
    2022-01-01
  • 簡(jiǎn)單分析MySQL中的primary key功能

    簡(jiǎn)單分析MySQL中的primary key功能

    這篇文章主要介紹了MySQL中的primary key功能,包括講到了其對(duì)InnoDB使用的影響,需要的朋友可以參考下
    2015-05-05
  • MySQL修改默認(rèn)存儲(chǔ)引擎的實(shí)現(xiàn)方法

    MySQL修改默認(rèn)存儲(chǔ)引擎的實(shí)現(xiàn)方法

    下面小編就為大家?guī)硪黄狹ySQL修改默認(rèn)存儲(chǔ)引擎的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-03-03
  • 3步搞定純真IP數(shù)據(jù)導(dǎo)入到MySQL的方法詳解

    3步搞定純真IP數(shù)據(jù)導(dǎo)入到MySQL的方法詳解

    免編程,3步搞定純真IP數(shù)據(jù)導(dǎo)入到MySQL詳解,好多做ip地址查詢的朋友用的到。
    2009-10-10

最新評(píng)論