导读:本期聚焦于小宵创作的《什么是死锁?MySQL死锁产生的原因和避免方式有哪些》,敬请观看详情。当两个事务各自持有对方需要的锁并互相等待时,数据库就会陷入死锁状态,MySQL会主动回滚其中一个事务。死锁并非MySQL独有,它是并发控制中典型的资源循环等待现象。在InnoDB引擎里,死锁大多发生在多个事务以不同顺序更新多张表或同一张表的不同行时。了解锁的兼容矩阵和事务隔离级别,有助于从业务设计层面减少死锁概率。出现死锁后应用应捕获异常并重试,而不是认为数据已丢失。

死锁是指两个或多个事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。具体到MySQL的InnoDB存储引擎,当事务A持有某行记录的排他锁并请求事务B持有的锁,同时事务B也在等待事务A已持有的锁,就会形成循环等待链,数据库无法自行解开这种僵局。MySQL的死锁检测机制会在发现循环等待后,选择一个代价较小的事务进行回滚,从而让其他事务继续执行。

什么是死锁?MySQL死锁产生的原因和避免方式有哪些

死锁的底层原理与InnoDB锁机制

InnoDB实现了行级锁,主要包括共享锁(S锁)和排他锁(X锁)。共享锁允许事务读取一行,多个事务可同时持有;排他锁则用于修改,同一时刻只能有一个事务持有。锁的兼容性矩阵决定了不同锁之间能否共存:S锁与S锁兼容,S锁与X锁互斥,X锁与任何锁都互斥。当事务执行UPDATEDELETE或显式SELECT ... FOR UPDATE时,会对涉及行加X锁。

除了记录锁,InnoDB还有间隙锁(Gap Lock)和临键锁(Next-Key Lock)。在可重复读隔离级别下,为了防止幻读,InnoDB默认使用临键锁,它锁定记录本身及前面的间隙。这意味着即使某行不存在,事务也能锁住一个范围,从而更容易在并发插入或更新时引发死锁。理解这些锁的类型,是分析死锁成因的前提。

当多个事务以相反顺序访问资源时,死锁极易发生。例如事务一先更新用户表再更新订单表,事务二先更新订单表再更新用户表,在并发场景下二者可能分别持有对方所需的锁。InnoDB内部维护了一个等待图(wait-for graph),当检测到图中存在环时即判定为死锁,随后抛出错误码1213并回滚其中一个事务。

MySQL死锁产生的常见原因与示例

最常见的死锁场景是不同的加锁顺序。下面通过两段伪代码展示问题所在。第一段事务先锁用户后锁订单,第二段则相反,高并发时就会冲突。

-- 事务A
START TRANSACTION;
UPDATE user SET balance=balance-100 WHERE id=1;
UPDATE order SET status=1 WHERE id=10;
COMMIT;

-- 事务B
START TRANSACTION;
UPDATE order SET status=1 WHERE id=10;
UPDATE user SET balance=balance-100 WHERE id=1;
COMMIT;

另一个原因是间隙锁与插入意向锁的冲突。在可重复读级别下,事务A对某个范围加间隙锁,事务B尝试插入该范围的新记录时需要获取插入意向锁,二者互斥;若此时A又反过来请求B持有的记录锁,便形成死锁。此外,二级索引上的加锁也会回溯到主键,增加锁的复杂度。

还有一类死锁来自外键约束。当子表插入数据时会去父表加S锁校验存在性,若父表事务同时更新子表相关记录,也可能交叉等待。通过SHOW ENGINE INNODB STATUS可以查看最近一次死锁的详细信息,包括每个事务持有的锁和等待的锁,对排查非常有帮助。

如何避免与解决MySQL死锁

最根本的避免方式是统一资源的访问顺序。所有业务代码对多张表或多行记录的更新,都应约定相同的顺序,比如永远先改user再改order。这样就不会出现循环等待。同时应尽量缩短事务长度,把非数据库操作(如远程调用、文件处理)移到事务之外,减少锁的占用时间。

在隔离级别上,如果业务允许,可考虑将隔离级别降为读已提交(READ COMMITTED),此时InnoDB会禁用间隙锁,仅用记录锁,能显著降低死锁概率,但需自行处理幻读风险。另外,为频繁更新的表建立合适索引也很关键,因为缺乏索引会导致锁升级为表级锁或扫描更多行,扩大锁冲突面。

应用层必须做好死锁重试。当捕获到MySQL返回的1213错误时,不应视作严重故障,而应使用指数退避策略重新执行该事务。以下Java片段演示了基础的重试逻辑:

int retries = 3;
while (retries-- > 0) {
    try {
        executeTransaction();
        break;
    } catch (SQLException e) {
        if (e.getErrorCode() == 1213) {
            Thread.sleep(50);
            continue;
        }
        throw e;
    }
}

最后,定期通过监控工具收集死锁日志,分析高频死锁的SQL模式,从表结构或业务流程上做优化。死锁在并发系统中难以彻底消失,但通过上述手段可以将其频率降到可接受范围,保障系统稳定。

死锁MySQL死锁事务锁修改时间:2026-08-18 17:18:13

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。