news 2026/6/15 12:47:46

MySQL 什么情况下会产生死锁?为什么会死锁?以及 Online DDL 解决了什么问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL 什么情况下会产生死锁?为什么会死锁?以及 Online DDL 解决了什么问题

前言

死锁不是“偶发事故”,而是设计问题

在实际业务中,很多同学第一次接触 MySQL 死锁,往往是在日志中看到一句:

Deadlock found when trying to get lock; try restarting transaction

然后的反应通常是:

  • “是不是 MySQL 的 bug?”
  • “加索引能不能解决?”
  • “把事务拆小一点行不行?”

事实上,死锁不是 MySQL 的 bug,而是 InnoDB 锁设计 + 业务访问模式共同作用的必然结果

本文将从一个非常真实的业务场景出发,深入剖析:

  • MySQL 在什么情况下会产生死锁
  • InnoDB 各类锁是如何协同工作的
  • 为什么SELECT ... FOR UPDATE + INSERT特别容易死锁
  • 幻读与 Next-Key Lock 在其中扮演了什么角色
  • Online DDL 到底解决了什么问题(以及没解决什么)

一、一个非常典型的业务场景

场景描述:资源唯一性校验

假设有一张表resource,业务语义是:某个资源只能被创建一次

CREATE TABLE resource ( id BIGINT PRIMARY KEY AUTO_INCREMENT, resource_key VARCHAR(64) NOT NULL, UNIQUE KEY uk_resource_key (resource_key) ) ENGINE=InnoDB;

常见的业务代码逻辑是:

BEGIN; SELECT * FROM resource WHERE resource_key = 'A' FOR UPDATE; -- 如果不存在 INSERT INTO resource(resource_key) VALUES ('A'); COMMIT;

乍一看,这段逻辑非常严谨

  • 使用事务
  • 使用SELECT FOR UPDATE防止并发插入
  • 有唯一索引兜底

但在高并发下,这正是死锁高发现场

二、InnoDB 锁体系总览(先有全局认知)

在分析死锁之前,我们必须先明确 InnoDB 中到底有哪些锁。

记录锁(Record Lock)

  • S 型记录锁(共享锁)

    • 多个事务可以同时持有

    • 用于普通SELECT

  • X 型记录锁(排他锁)

    • 同一条记录只能有一个事务持有

    • 用于UPDATE / DELETE / SELECT FOR UPDATE

记录锁只锁已经存在的记录

间隙锁(Gap Lock)

  • 锁定的是索引记录之间的“区间”
  • 不锁具体记录,只锁“范围”
  • 用来防止幻读

例如索引中存在:

(10) —— (20)

Gap Lock 可以锁住(10, 20)这个区间。

Next-Key Lock(临键锁)

InnoDB 中最重要、也是最容易导致死锁的锁

  • =记录锁 + 间隙锁
  • 锁定区间:(prev_key, current_key]
  • 是 InnoDB RR(可重复读)隔离级别的默认行为

SELECT ... FOR UPDATE
索引范围查询下,几乎一定会触发 Next-Key Lock。

插入意向锁(Insert Intention Lock)

  • 一种特殊的间隙锁
  • S 型锁
  • 用于表示:

“我想在这个 gap 里插入一条记录”

重点:
插入意向锁与 Gap Lock / Next-Key Lock 是冲突的

意向锁(Intention Lock)

  • 表级锁(IS / IX)
  • 表示事务“将要”在某些记录上加锁
  • 用于提高锁冲突判断效率
  • 不直接参与死锁,但几乎所有行锁都会先加意向锁

隐式锁 & 显式锁

  • 隐式锁
    插入时,事务尚未真正加行锁,但通过事务 ID 隐含保护

  • 显式锁
    当其他事务需要访问这条记录时,隐式锁会升级为显式锁

隐式锁升级的过程,是很多死锁的导火索

元数据锁(MDL)

  • 控制表结构的并发访问
  • DDL / DML 之间的互斥
  • 是 Online DDL 要重点解决的问题(后文详述)

三、死锁是如何一步一步发生的?

下面我们用事务 A / 事务 B复盘整个死锁过程。

第一步:两个事务同时进入

事务 A 事务 B BEGIN; BEGIN;

第二步:SELECT FOR UPDATE

SELECT * FROM resource WHERE resource_key = 'A' FOR UPDATE;

假设此时表中还没有resource_key = 'A'的记录

会发生什么?

  • InnoDB 在唯一索引uk_resource_key
  • A所在的索引区间加上X 型 Next-Key Lock
  • 锁住的是一个“未来可能插入 A 的区间”

结果是:

事务 A:持有 Next-Key Lock(X) 事务 B:也尝试加同一个 Next-Key Lock(X)

注意:
Next-Key Lock 是互斥的

所以:

  • 事务 A 成功
  • 事务 B 被阻塞,等待 A 释放锁

第三步:事务 A 执行 INSERT

INSERT INTO resource(resource_key) VALUES ('A');

此时发生了一个非常关键的事情:

插入位置落在已被 Next-Key Lock 锁住的 gap

InnoDB 会尝试:

  • 将插入操作的隐式锁

  • 转换为显式锁

同时需要获取一个插入意向锁(S 型)

第四步:锁冲突出现

关键冲突点来了:

  • 插入意向锁(S)
  • 与事务 B 持有的Next-Key Lock(X)冲突

于是:

事务 A:等待 B 释放 Next-Key Lock 事务 B:等待 A 释放 Next-Key Lock

循环等待成立,死锁产生

InnoDB 如何处理?

  • InnoDB 会通过死锁检测线程
  • 选择代价较小的事务回滚
  • 抛出死锁异常

四、这类死锁的本质原因是什么?

总结一句话:

Next-Key Lock + 插入意向锁 + 并发存在性检查 = 死锁温床

更具体地说:

  1. SELECT FOR UPDATE在 RR 下锁的是“范围”,不是“记录”
  2. 间隙锁之间是兼容的
  3. 插入时需要插入意向锁
  4. 插入意向锁与 Next-Key Lock 冲突
  5. 多事务互相等待,形成环

五、幻读与 Next-Key Lock 的关系

什么是幻读?

事务 A:第一次查询没有记录 事务 B:插入一条新记录 事务 A:第二次查询发现“多了一条”

这就是幻读。

InnoDB 如何解决幻读?

  • RR 隔离级别下
  • 使用Next-Key Lock
  • 锁住“可能出现新记录的范围”

解决幻读的代价,就是更复杂的锁冲突模型

六、Online DDL 解决了什么问题?

传统 DDL 的问题

在早期 MySQL 版本中:

ALTER TABLE resource ADD COLUMN ext VARCHAR(64);

会:

  • 持有排他 MDL 锁
  • 阻塞所有:
  • SELECT

  • INSERT

  • UPDATE

  • DELETE

对线上业务是灾难性的

Online DDL 的核心目标

Online DDL 的目标不是“无锁”,而是:

最小化 DDL 对 DML 的影响

Online DDL 做了哪些事情?

MDL 锁拆分

  • 大部分时间使用共享 MDL
  • 只在最终切换阶段使用短暂排他锁

拷贝/重建过程异步化

  • 后台重建数据
  • 前台继续处理读写

引入 inplace / instant 算法

  • MySQL 8.0 中大量 DDL 不再重建表

Online DDL 能解决死锁吗?

不能

Online DDL 解决的是:

  • 表结构变更期间的阻塞问题
  • MDL 导致的长时间不可用

但它:

  • 不会改变 InnoDB 行锁模型
  • 不会避免 Next-Key Lock
  • 不会消除业务逻辑导致的死锁

七、如何在业务层面规避这类死锁?

设计层面

避免:

SELECT ... FOR UPDATE + INSERT

改为:

  • 直接 INSERT
  • 依赖唯一索引
  • 捕获重复键异常

降低锁范围

  • 精确命中唯一索引
  • 避免范围扫描
  • 减少 Next-Key Lock 触发概率

事务控制

  • 事务尽量短
  • 不要在事务中做无关操作
  • 固定访问顺序,减少交叉等待

总结

为什么会死锁?

  • InnoDB 为了解决幻读,引入了 Next-Key Lock

  • Next-Key Lock + 插入意向锁存在天然冲突

  • 并发事务在特定业务模式下形成循环等待

Online DDL 的定位

  • 解决的是DDL 阻塞问题

  • 不是行锁死锁的“银弹”

一句话结论

MySQL 死锁不是偶然,而是锁模型与业务访问模式的必然结果

理解锁,才能真正写出高并发安全的 SQL。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 9:33:36

Jenkins初识

1.安装jdk 参考: https://blog.csdn.net/Pan_peter/article/details/128845347 https://cloud.tencent.com/developer/article/1888380 检索可用包 sudo yum search java |grep jdk 发现可用的版本都比较老,所以安装个比较新的jdk版本,本…

作者头像 李华
网站建设 2026/6/15 4:26:44

Dsc1103ni5-200.0000,高性能、低抖动的振荡器, 现货库存

型号介绍 今天我要向大家介绍的是 HCI 的一款振荡器——Dsc1103ni5-200.0000。 它的核心优势是“低抖动”,其RMS相位抖动典型值小于1ps,这对于高速数据传输至关重要,能确保信号清晰无误。同时,它对电源噪声有很强的抑制能力…

作者头像 李华
网站建设 2026/6/15 9:35:42

为什么90%的候选分子折戟临床前?Agent实验设计的6大致命误区

第一章:为什么90%的候选分子折戟临床前? 在新药研发领域,从成千上万个候选分子中筛选出一个成功上市的药物,犹如大海捞针。据统计,超过90%的候选分子在临床前阶段即被淘汰,其背后原因错综复杂,涉…

作者头像 李华
网站建设 2026/6/15 0:06:13

AI手机接管你的指尖:系统级智能体是效率革命,还是权限黑洞?

【摘要】系统级智能体正将手机从工具重塑为自主代理,带来极致效率的同时,也引发了关于权限、数据与用户主权的深刻拷问。这不仅是技术迭代,更是移动互联网权力的范式转移。引言“我要吃一份猪脚饭。”当这句简单的语音指令,不再需…

作者头像 李华
网站建设 2026/6/14 20:20:48

护理Agent提醒频率怎么定?三甲医院实测数据告诉你答案

第一章:护理Agent提醒频率的核心挑战在构建智能护理Agent系统时,提醒频率的设定成为影响用户体验与护理效果的关键因素。过高频率的提醒可能导致用户疲劳甚至忽略关键通知,而过低的频率则可能延误必要的护理干预。因此,如何在及时…

作者头像 李华
网站建设 2026/6/15 11:45:37

python_django基于销售趋势的餐饮管理系统_9qurrf09可视化大屏

文章目录系统截图项目技术简介可行性分析主要运用技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式!系统截图 python_django基于销售趋势的餐饮管理系统_9qurrf09可视化大屏 项目技术简介 Python版本&…

作者头像 李华