news 2026/3/4 19:38:11

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/3/4 1:33:14

仅限前沿研究者:量子 Agent 最新7种优化策略首次公开

第一章:量子 Agent 算法优化的前沿概览随着量子计算与人工智能的深度融合,量子 Agent 在复杂环境中的决策优化展现出前所未有的潜力。这类智能体利用量子态叠加、纠缠和干涉等特性,在搜索空间巨大的任务中实现指数级加速,尤其在动…

作者头像 李华
网站建设 2026/3/4 0:11:16

【计算机毕业设计案例】基于springboot+微信小程序的的提交交通违法有奖曝光平台(程序+文档+讲解+定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/3/4 12:58:01

vue和springboot框架开发的小程序 宠物领养系统_c27l9jc8

文章目录具体实现截图主要技术与实现手段关于我本系统开发思路java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式!具体实现截图 同行可拿货,招校园代理 vueSpringboot宠物领养系统_车7 框架开发的小程序和 …

作者头像 李华
网站建设 2026/3/4 12:20:49

多模态Agent在医疗场景落地难?关键在于这4个权重平衡点

第一章:医疗多模态 Agent 的权重平衡概述在构建面向医疗场景的多模态智能体(Agent)时,如何有效融合来自文本、影像、生理信号等多种模态的信息成为核心挑战。权重平衡机制决定了不同模态输入对最终决策的贡献程度,直接…

作者头像 李华
网站建设 2026/3/4 7:29:36

影刀RPA一键批量上传Zozone商品视频,效率提升800%![特殊字符]

影刀RPA一键批量上传Zozone商品视频,效率提升800%!🚀还在手动一个个上传商品视频?每天重复选择文件、填写信息、等待上传,浪费生命又容易出错?今天带你用影刀RPA实现视频批量上传自动化,100个视…

作者头像 李华
网站建设 2026/3/4 14:04:49

【精准护理新标准】:基于患者行为画像的自适应提醒频率算法详解

第一章:精准护理中提醒频率的演进与挑战在数字化医疗快速发展的背景下,精准护理中的提醒频率管理已成为提升患者依从性与治疗效果的关键环节。早期的提醒系统多依赖固定周期通知,如每日定时发送用药提醒,缺乏对个体差异和临床情境…

作者头像 李华