首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >MySQL并发控制秘籍:MVCC与锁的那些事儿

MySQL并发控制秘籍:MVCC与锁的那些事儿

作者头像
java金融
发布2025-11-24 14:50:23
发布2025-11-24 14:50:23
110
举报
文章被收录于专栏:java金融java金融

在MySQL的世界里,多个用户同时操作数据是家常便饭。就像图书馆里既有读者在看书,又有管理员在更新藏书,若没有合理的规则,混乱便会随之而来——有人看到没定稿的内容,有人发现刚看过的书内容变了,甚至有人统计的人数突然多了几个“幽灵”。而MVCC(多版本并发控制)、间隙锁等机制,就是MySQL的“图书馆管理规则”,专门解决这些并发难题。今天我们就用最通俗的方式,把这些核心概念讲明白。

一、先搞懂:并发操作会遇到哪些“坑”?

在讲解决方案前,我们得先清楚问题的本质。当多个事务同时操作数据时,容易出现三类典型问题:脏读、不可重复读和幻读。我们用生活场景一一对应,就像看故事一样简单。

1. 脏读:看到别人“没发出去的草稿”

小张写了封辞职信,中途保存了草稿但没提交给老板,同事小李偷偷看到了这封草稿,立马传开“小张要走了”。结果小张反悔删了草稿,小李的消息就成了“假新闻”——这就是生活中的“脏读”。

对应到数据库中:事务A修改了一条数据(比如把工资从1万改成8千),但还没提交;事务B这时候读到了这个未提交的修改结果。如果事务A最终反悔回滚,事务B读到的就是“根本不存在”的脏数据,基于这个数据做决策必然出错。

2. 不可重复读:同一本书,翻两次内容不一样

你借了本《MySQL实战》,第一次翻到第10页看到“主键不能为NULL”,喝水回来再翻同一页,内容却变成“主键可以为NULL”——原来管理员趁你离开换了新版。这种“同一内容前后不一致”的情况,就是不可重复读。

数据库场景中:事务A在同一个事务里两次查询同一行数据,第一次查到工资1万,中途事务B修改工资为8千并提交,事务A第二次查询时,数据就变了。这会让事务A对数据的一致性产生困惑。

3. 幻读:统计人数时,突然多了个“幽灵”

班主任统计不及格人数,第一次查全班都及格,刚要汇报时再查,却多了1个不及格的学生——原来是教务系统刚录入了一个转学生。这个“凭空出现”的记录,就像幽灵一样,这就是幻读。

在数据库中:事务A执行范围查询(比如“查询余额大于500的账户”),第一次得到1条结果;事务B插入一条余额800的新记录并提交,事务A再次执行相同查询,结果变成了2条。幻读的核心是“结果集行数变化”,和不可重复读的“单行内容变化”有本质区别。

二、MVCC:给数据拍“快照”,让事务互不打扰

面对这些并发问题,MySQL的InnoDB引擎祭出了“MVCC(多版本并发控制)”这个大杀器。它的核心思想很简单:不直接覆盖旧数据,而是为数据保留多个版本,让不同事务看到自己“该看”的版本。

1. 用图书馆比喻理解MVCC

图书馆里有本《MySQL入门》,最新版是第3版。小明上周借走了第2版开始阅读,你今天把书更新到了第3版。如果图书馆只留最新版,小明的阅读就会被打断;而MVCC相当于图书馆同时保存第2版和第3版,小明继续看自己借的版本,你用你的新版本,互不干扰。

2. MVCC在MySQL中的实现逻辑

InnoDB会给每行数据偷偷加两个“隐藏字段”:

  • DB_TRX_ID:记录这行数据最后被哪个事务修改过,相当于给数据版本打个“修改者标签”;
  • DB_ROLL_PTR:指向“回滚日志(undo log)”,这里保存着数据的旧版本,就像数据的“历史快照档案”。

当你开启一个事务执行查询时,MySQL会根据事务开始时间生成一个“一致性视图(Read View)”,然后通过这个视图去匹配数据版本——只返回“在事务开始前已提交”或“本事务修改”的版本。这样一来,即使其他事务在修改数据,你看到的始终是自己事务开始时的快照,自然不会被干扰。

3. MVCC的“超能力”

  • 读不加锁:普通查询(快照读)不用加锁,性能远超“一查就锁表”的方式;
  • 解决核心问题:在InnoDB默认的“可重复读(REPEATABLE READ)”隔离级别下,能彻底避免脏读和不可重复读;
  • 高并发友好:读写互不阻塞,多个事务可以同时“安心工作”。

三、幻读的“终极解决方案”:间隙锁与临键锁

MVCC能解决脏读和不可重复读,但对付幻读还不够——因为幻读是“行数变化”,MVCC的快照读只能屏蔽已提交的修改,却挡不住新插入的记录。这时候,“锁”就要登场了,其中最关键的就是间隙锁(Gap Lock)和临键锁(Next-Key Lock)。

1. 先分清:快照读与当前读

InnoDB的读操作分两类,处理幻读的方式完全不同:

读类型

示例

是否加锁

能否看到新数据

快照读

普通SELECT

看不到(靠MVCC快照)

当前读

SELECT ... FOR UPDATE、UPDATE、DELETE

能看到(需最新数据)

简单说:快照读是“看历史”,用MVCC防幻读;当前读是“看现在”,必须用锁防幻读。

2. 间隙锁:锁住“空隙”,阻止插入

间隙锁不是锁具体的某一行,而是锁住索引之间的“空白区域”。比如表中主键id有5、10、20这三个值,那么索引间隙就包括(-∞,5)、(5,10)、(10,20)、(20,+∞)。这些“空隙”就是可能插入新记录的地方,锁住它们就能从根源上杜绝幻读。

举个例子:执行SELECT * FROM t WHERE id >10 AND id <25 FOR UPDATE,InnoDB会:

  • 给id=20这行加“记录锁”,防止别人修改或删除它;
  • 给(10,20)和(20,+∞)这两个间隙加“间隙锁”,阻止别人插入id=15、22、30等新记录。

3. 临键锁:间隙锁+记录锁的“组合拳”

临键锁是InnoDB的默认锁类型,本质是“间隙锁+记录锁”的组合,锁定的区间是“左开右闭”。比如对id=10加临键锁,锁定的是(5,10]区间——既包括id=10这条记录,也包括它前面的间隙(5,10)。这样一来,既保护了现有记录不被修改,又防止了新记录插入到间隙中,是防幻读的核心武器。

有个特殊情况要注意:如果用唯一索引(比如主键)做等值查询,且查询的值存在,InnoDB会自动把临键锁“降级”为记录锁——因为唯一索引能保证不会有重复值插入,没必要锁间隙,这样能提高并发性能。但如果查询的值不存在(比如查id=25,而表中最大id是20),则仍会锁住(20,+∞)这个间隙。

四、实用技巧:如何查看MySQL的锁信息?

遇到锁等待或死锁时,我们需要查看锁的状态来排查问题。MySQL提供了几个实用工具,新手也能轻松上手:

1. 快速查看引擎状态(推荐)

执行以下命令,能看到最新的死锁信息和活跃事务的锁情况:

代码语言:javascript
复制

SHOW ENGINE INNODB STATUS\G

在输出结果的“LATEST DETECTED DEADLOCK”部分,能清晰看到死锁双方的事务、持有的锁和等待的锁,帮你快速定位问题。

2. 用performance_schema查看详细锁信息(MySQL 5.7+)

先开启监控开关:

代码语言:javascript
复制

-- 开启锁监控
UPDATE performance_schema.setup_instruments 
SET ENABLED = 'YES' 
WHERE NAME LIKE '%wait/lock%';

-- 开启锁消费器
UPDATE performance_schema.setup_consumers 
SET ENABLED = 'YES' 
WHERE NAME LIKE '%locks%';

然后查询锁信息:

代码语言:javascript
复制

-- 查看当前持有的所有锁
SELECT * FROM performance_schema.data_locks;

-- 查看锁等待关系
SELECT * FROM performance_schema.data_lock_waits;

其中“LOCK_MODE”字段能显示锁的类型,比如“X,GAP”表示排他间隙锁,“X”表示排他记录锁,通过这些信息能快速判断锁的作用范围。

五、核心知识点速记

最后用一张表总结核心内容,帮你加深记忆:

概念

核心作用

关键特点

MVCC

解决脏读、不可重复读

数据多版本,读不加锁

间隙锁

阻止间隙插入新记录

锁“空隙”不锁具体行

临键锁

防幻读的默认锁

记录锁+间隙锁,左开右闭区间

快照读

读取历史快照

普通SELECT,靠MVCC实现

当前读

读取最新数据

加锁操作,靠间隙锁防幻读

其实MySQL的并发控制机制没那么可怕,记住“MVCC负责看不见变化,间隙锁负责插不进新数据”这个核心逻辑,很多问题就迎刃而解了。

最后再推荐下不会前端纯靠靠ai手工做的小程序。

零基础不会前端?我靠AI做出了一个育儿补贴计算器小程序!

往期精选

推荐👍 :Java高并发编程基础三大利器之CyclicBarrier

推荐👍Java高并发编程基础三大利器之CountDownLatch

推荐👍Java高并发编程基础三大利器之Semaphore

推荐👍Java高并发编程基础之AQS

推荐👍可恶的爬虫直接把生产6台机器爬挂了!

代码语言:javascript
复制
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构、等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java金融 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、先搞懂:并发操作会遇到哪些“坑”?
    • 1. 脏读:看到别人“没发出去的草稿”
    • 2. 不可重复读:同一本书,翻两次内容不一样
    • 3. 幻读:统计人数时,突然多了个“幽灵”
  • 二、MVCC:给数据拍“快照”,让事务互不打扰
    • 1. 用图书馆比喻理解MVCC
    • 2. MVCC在MySQL中的实现逻辑
    • 3. MVCC的“超能力”
  • 三、幻读的“终极解决方案”:间隙锁与临键锁
    • 1. 先分清:快照读与当前读
    • 2. 间隙锁:锁住“空隙”,阻止插入
    • 3. 临键锁:间隙锁+记录锁的“组合拳”
  • 四、实用技巧:如何查看MySQL的锁信息?
    • 1. 快速查看引擎状态(推荐)
    • 2. 用performance_schema查看详细锁信息(MySQL 5.7+)
  • 五、核心知识点速记
    • 往期精选
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档