Mysql 幻讀詳解

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

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

1. 什么是幻讀?

官方定義:幻讀指的是在一個事務內,相同的查詢在不同時間執行,返回了不同數量的行

這聽起來和“不可重復讀”很像,但它們有關鍵區別:

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

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

簡單比喻

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

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


2. 幻讀發生的場景與例子

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

我們來看一個經典的例子。

表結構

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

時間線

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

例子分析

  1. T2時刻:事務A第一次查詢,得到2條記錄。

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

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

  4. T6時刻:事務A再次查詢。因為之前的UPDATE操作屬于當前事務的修改,所以MVCC規則允許它看到自己的修改。于是,它神奇地看到了三條記錄!幻讀就在這一刻發生了

這個例子展示了InnoDB中幻讀最典型的特征:即使是在默認的RR隔離級別下,先快照讀,再當前讀進行寫操作,可能會意外地影響新插入的行,從而導致數據不一致。


3. 解決方案:Next-Key Lock 鎖機制

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

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

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

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

如何解決幻讀?

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

讓我們重演時間線,但這次事務A加鎖查詢

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

發生了什么?

當事務A執行?SELECT ... FOR UPDATE?時,InnoDB會為其加Next-Key Lock。假設?salary?上有二級索引?idx_salary,它可能會鎖住以下區間:

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

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

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

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


總結與最佳實踐

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

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

推薦一個非常好用的工具集合:在線工具集合 - 您的開發助手

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/96314.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/96314.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/96314.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何生成 GitHub Token(用于 Hexo 部署):保姆級教程+避坑指南

如何生成 GitHub Token&#xff08;用于 Hexo 部署&#xff09;&#xff1a;保姆級教程避坑指南 前置說明&#xff1a;為什么需要 GitHub Token&#xff1f; 在使用 Hexo 部署博客到 GitHub Pages 時&#xff0c;你可能會遇到「密碼驗證失敗」或「需要雙重驗證」的問題——這…

常用加密算法之 AES 簡介及應用

相關系列文章 常用加密算法之 SM4 簡介及應用常用加密算法之 RSA 簡介及應用 引言 AES&#xff08;Advanced Encryption Standard&#xff0c;高級加密標準&#xff09;是一種??廣泛使用的對稱分組加密算法??&#xff0c;它使用相同的密鑰進行加密和解密操作&#xff0c…

Java面試問題記錄(一)

一、Java 核心基礎與進階1、我們知道 Java 中存在 “值傳遞” 和 “引用傳遞” 的說法&#xff0c;你能結合具體例子&#xff0c;說明 Java 到底是值傳遞還是引用傳遞嗎&#xff1f;這背后涉及到 JVM 中哪些內存區域的交互&#xff1f;Java中只有值傳遞&#xff0c;不存在引用傳…

Redis 主從復制、哨兵與 Cluster 集群部署

文章摘要 本文基于 VMware 虛擬機環境&#xff0c;詳細講解 Redis 高可用架構的核心組件與部署流程&#xff0c;涵蓋三大核心模塊&#xff1a;Redis 主從復制&#xff08;實現數據備份與讀寫分離&#xff09;、Redis 哨兵&#xff08;基于主從復制實現故障自動轉移&#xff0c;…

ElementUI 中 validateField 對部分表單字段數組進行校驗時多次回調問題

目錄 方案一&#xff1a;循環調用 Promise.all 合并結果 方案二&#xff1a;直接傳入數組字段 總結 在實際業務中&#xff0c;我們有時只需要對表單的部分字段進行校驗。ElementUI 提供的 validateField 方法支持單個字段&#xff0c;也支持字段數組&#xff0c;但在使用時…

Visual Studio 2026 震撼發布!AI 智能編程時代正式來臨

Visual Studio 2026 震撼發布&#xff01;AI 智能編程時代正式來臨 Visual Studio 2026 Insider圖標 開發者們的開發環境即將迎來前所未有的智能革命&#xff0c;微軟用Visual Studio 2026 重新定義了編碼體驗。 2025年9月10日&#xff0c;微軟正式推出了Visual Studio 2026 In…

Gamma AI:高效制作PPT的智能生成工具

你有沒有過這種崩潰時刻&#xff1f;領導讓你下午交一份產品介紹 PPT&#xff0c;你打開模板網站翻了半小時沒找到合適的&#xff0c;好不容易選了個模板&#xff0c;又得手動調整文字間距、搭配圖片&#xff0c;光是把數據做成圖表就花了一小時&#xff0c;最后趕出來的 PPT 還…

Python副業新玩法:用Flask搭小程序后端,躺賺被動收入的秘密

凌晨1點&#xff0c;林浩合上電腦時&#xff0c;手機彈出一條微信消息——是上周幫一家社區水果店搭的小程序后端&#xff0c;商家發來了當月的服務費到賬提醒。他靠在椅背上笑了&#xff1a;這是這個月第8筆“睡后收入”&#xff0c;加起來剛好覆蓋了下個月的房貸。半年前&…

基于PyQt5和阿里云TTS的語音合成應用開發實戰[附源碼】

項目概述 本文將詳細介紹一個基于PyQt5圖形界面框架和阿里云TTS(Text-to-Speech)服務的語音合成桌面應用程序的開發過程。該應用提供了完整的文字轉語音功能,包括多音色選擇、參數調節、實時試聽、語速調節和音頻下載等特性。 技術棧 前端界面: PyQt5 語音合成: 阿里云TTS服…

基于esp32c3 rust embassy 的墨水屏程序

EPD Reader 基于ESP32-C3的電子墨水屏閱讀器&#xff0c;支持ap 配網、sntp 時間同步、txt閱讀、天氣預報、顯示節假日信息、農歷顯示、自動休眠、web配置等功能。這是在另一個項目 一個rust embassy esp32c3 的練習項目-CSDN博客的基礎上修改的 。 界面比較粗糙&#xff0c;以…

Spring 單例測試及線程安全

創建一個賬戶類 package com.duanhw.demo22.account;import org.springframework.beans.factory.annotation.Value;//Service public class AccountService {Value("1000")private Integer balance;//存款public void deposit(Integer amount){int newbalance balanc…

【vue】組件寬度調整失效后,調整的方法

父容器布局限制 若組件放置在柵格布局&#xff08;如display: grid&#xff09;或彈性容器中&#xff0c;父元素的寬度限制可能導致子組件寬度失效。解決方案是為父容器設置明確的寬度&#xff0c;或通過百分比布局實現自適應16。例如&#xff1a; <div style"width:…

Java 在Word 文檔中插入頁眉頁腳:一份實用的編程指南

在現代企業應用中&#xff0c;Java 開發者經常需要處理各種文檔操作&#xff0c;其中對 Word 文檔的自動化處理尤為常見。無論是生成報告、合同還是其他商業文檔&#xff0c;頁眉頁腳作為文檔結構的重要組成部分&#xff0c;承載著公司 Logo、頁碼、版權信息等關鍵內容。手動添…

深入解析Dart虛擬機運行原理

Dart虛擬機運行原理 一、Dart虛擬機 1.1 引言 Dart VM是一種虛擬機&#xff0c;為高級編程語言Dart提供執行環境&#xff0c;但這并意味著Dart在D虛擬機上執行時&#xff0c;總是采用解釋執行或者JIT編譯。 例如還可以使用Dart虛擬機的AOT管道將Dart代碼編譯為機器代碼&#xf…

光譜相機在AI眼鏡領域中的應用

一、核心應用場景?健康監測系統??實時生理指標分析?&#xff1a;通過眼周皮膚光譜特征&#xff0c;監測血氧(SpO?)和血紅蛋白變化&#xff0c;精度可達2%?血糖無創檢測?&#xff1a;近紅外光譜(900-1700nm)分析淚液成分&#xff0c;臨床測試相關系數R0.87?疲勞度評估?…

如何通過url打開本地文件文件夾

安裝部署 https://github.com/jixn-hu/notion_link_opener 這是我自己開發的一個后端服務&#xff0c;要一直開著 部署好后 會打開一個前端頁面填下好你文件或者文件夾 點擊生成短鏈就可以直接打開本地的文件夾了

第一篇:如何在數組中操作數據【數據結構入門】

記錄以下自己重溫數據結構的筆記&#xff0c;附帶自己實現的C代碼&#xff0c; 其中部分Python代碼是網上教程里的&#xff0c;順手粘貼過來&#xff0c;做一對比/ &#xff08;Python確實簡潔&#xff0c;但是C更好理解不是嗎哈哈哈&#xff09;數組的定義 數組&#xff1a;線…

基于STM32的單片機開發復盤

硬件介紹 底盤&#xff1a;幻爾阿克曼底盤&#xff1b;2個直流霍爾電機、1個PWM舵機開發板&#xff1a;幻爾Ros Controller V1.2&#xff08;STM32F407VET6&#xff09;電源&#xff1a;因為是學習階段&#xff0c;沒有配電池&#xff0c;使用120W可調電源&#xff08;3V~12V&a…

面試常問:注冊中心宕機,遠程調用還能成功嗎?

在微服務架構里&#xff0c;注冊中心&#xff08;像 Nacos、Eureka、Consul 等&#xff09;是服務發現與治理的核心。可要是注冊中心突然宕機&#xff0c;微服務間的遠程調用還能順利進行嗎&#xff1f;這是面試時很常被問到的問題&#xff0c;下面我們就來深入剖析。一、遠程調…

《用 Python 和 Matplotlib 繪制折線圖:從入門到實戰的可視化指南》

《用 Python 和 Matplotlib 繪制折線圖:從入門到實戰的可視化指南》 一、引言:數據可視化的力量,從一張折線圖開始 在我多年的開發與教學經歷中,最常被問到的問題之一是:“如何讓數據更直觀?”我的答案始終如一:用圖說話。而在眾多圖表類型中,折線圖以其簡潔、清晰的…