news 2026/7/10 17:49:52

如何避免MySQL死锁?资深DBA的9条黄金法则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何避免MySQL死锁?资深DBA的9条黄金法则

死锁是数据库里很常见的问题:两个或多个事务互相等待对方释放锁,结果谁也动不了。

MySQL的InnoDB引擎会自己自动检测死锁,并且回滚其中一个事务来解决,但这种情况如果经常遇到的话,会很影响性能和用户体验。

其实,只要注意一些设计细节,就能大大减少甚至避免死锁。

下面是几个最实用的方法:


1. 事务要短,动作要快

事务越长,锁住数据的时间就越久,别人就越容易“撞上”你。

正确做法:只在事务里做必要的数据库操作,别把业务逻辑(比如调接口、算数据)塞进去。

sql

-- 不推荐:事务中混杂业务逻辑 START TRANSACTION; SELECT * FROM users WHERE id = 1; -- 假设此处有耗时的业务处理... UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; UPDATE orders SET status = 'paid' WHERE user_id = 1; COMMIT; -- 推荐:事务只包含必要数据库操作 START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; UPDATE orders SET status = 'paid' WHERE user_id = 1; COMMIT;


2. 所有事务按同一个顺序操作表

这是避免死锁最有效的一招!

比如:如果多个事务都要改 users 和 orders 表,那就统一先改 users,再改 orders。不要有的先改 users,有的先改 orders。

sql

-- 所有地方都这样写: UPDATE users SET ... WHERE id = 1; UPDATE orders SET ... WHERE user_id = 1;

只要顺序一致,就不会出现“A等B、B等A”的循环等待。


3. 给表加合适的索引

InnoDB 的行锁是靠索引来实现的。如果查询没用到索引,MySQL 就可能锁住整张表(或很多无关的行),大大增加死锁风险。

建议

  • 经常用来查或更新的字段(比如 user_id)要建索引。
  • 用 EXPLAIN 看看 SQL 是否命中索引。
sql

CREATE INDEX idx_user_id ON orders(user_id);


4. 别用太高的隔离级别(除非必要)

MySQL 默认是 REPEATABLE READ,它会加“间隙锁”,防止幻读,但也更容易死锁。

如果你的业务能接受“读已提交”(比如允许看到别人刚提交的数据),可以改成:

sql

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

这样锁的范围更小,死锁概率更低。


5. 显式加锁时要小心

如果你要用 SELECT ... FOR UPDATE 锁行,一定要确保:

  • 条件能命中索引;
  • 锁的行尽量少;
  • 事务尽快结束。
sql

-- 安全:通过主键或索引锁定一行 SELECT * FROM accounts WHERE user_id = 1 FOR UPDATE;

如果 user_id 没索引,这条语句可能锁住成千上万行!


6. 应用层要有重试机制

死锁偶尔还是会发生。这时候,应用应该:

  • 捕获死锁错误(MySQL 错误码 1213 或 SQLSTATE '40001');
  • 自动重试几次(比如最多 2~3 次);
  • 每次重试前等一小会儿(比如 100ms、200ms…)。
java

// 伪代码示例 for (int i = 0; i < 3; i++) { try { doDatabaseUpdate(); break; // 成功就退出 } catch (DeadlockException e) { sleep(100 * (i + 1)); // 等一下再试 } }


7. 大批量更新要分批做

一次更新几万行?这很容易锁住大量数据,引发死锁或卡顿。

正确做法:每次只改 500~1000 行,改完提交,再继续。

sql

-- 分批更新 UPDATE large_table SET status = 'done' WHERE create_time < '2023-01-01' AND status != 'done' LIMIT 1000; -- 循环执行,直到没有数据可更新


8. 避免热点数据被频繁修改

比如一个全局计数器,所有请求都去 UPDATE counter SET value = value + 1,那这一行就成了堵点。

解决办法:用分桶计数。

sql

-- 把计数分散到 10 个桶里 UPDATE counter_buckets SET value = value + 1 WHERE name = 'views' AND bucket = FLOOR(RAND() * 10); -- 查总数时再加起来 SELECT SUM(value) FROM counter_buckets WHERE name = 'views';


9. 出问题了怎么查?

看最近一次死锁详情:

sql

SHOW ENGINE INNODB STATUS;

找LATEST DETECTED DEADLOCK部分。

查当前正在运行的事务(MySQL 8.0+):

sql

SELECT * FROM performance_schema.data_locks; SELECT * FROM information_schema.INNODB_TRX;


总结

1.事务要短:别拖着不提交。 2.顺序要一致:所有人按相同顺序改表。 3.索引要到位:避免锁太多无关数据。 4.出错要重试:应用层兜底处理死锁。 5.大批量要分批:别一次锁太多行。

死锁没法完全杜绝,但只要做好这些,基本就不会再被它困扰了!

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

深入理解 C# 中 new 关键字的三重核心语义

在 C# 编程中&#xff0c;new 是一个几乎每天都会用到的关键字&#xff0c;但它的职责并不单一。根据使用场景的不同&#xff0c;new 在语言层面承担着 三种完全不同的语义角色&#xff1a; 1. 作为运算符&#xff1a; 创建对象或结构体实例 2. 作为修饰符&#xff1a; 隐藏基类…

作者头像 李华
网站建设 2026/7/10 15:32:32

Android防撤回终极指南:免Root永久告别消息撤回烦恼

还在为错过重要消息而懊恼吗&#xff1f;当同事撤回工作安排、朋友撤回关键信息时&#xff0c;你是否感到无比困扰&#xff1f;Anti-recall防撤回工具正是为解决这一痛点而生&#xff0c;让你从此不再错过任何被撤回的内容。作为一款免Root的Android防撤回工具&#xff0c;它能…

作者头像 李华
网站建设 2026/7/7 23:51:21

告别低效详情:A+页面重构亚马逊转化逻辑

在亚马逊平台上&#xff0c;A页面正在成为品牌差异化竞争的核心战场&#xff0c;这一工具已从简单的图文展示&#xff0c;演变为集智能创作、交互体验与数据优化于一体的品牌中枢系统&#xff0c;每一次功能升级&#xff0c;都标志着电商沟通从“信息告知”向“价值感知”的深度…

作者头像 李华
网站建设 2026/7/9 20:27:06

汇编语言全接触-30.Win32调试API三

在本章中&#xff0c;我们将继续探讨win32调试api。特别地&#xff0c;我们将学习如何去跟踪被调试程序.下载 例子. 理论:如果你以前使用过调试器&#xff0c;那么你应对跟踪比较熟悉。当"跟踪"一个程序时&#xff0c;程序在每执行一条指令后将会停止&#xff0c;这使…

作者头像 李华
网站建设 2026/7/9 23:53:37

LobeChat做市场调研分析可行吗?真实案例验证

LobeChat做市场调研分析可行吗&#xff1f;真实案例验证 在企业越来越依赖数据驱动决策的今天&#xff0c;市场调研早已不再是“发问卷、看报表”的简单流程。面对社交媒体评论、竞品发布会纪要、行业白皮书等海量非结构化文本&#xff0c;传统人工分析方式不仅耗时费力&#x…

作者头像 李华
网站建设 2026/7/6 19:52:18

小白程序员入门AI大模型的完整学习路径与资源合集_AI大模型学习路线

本文提供了AI大模型的完整学习路线&#xff1a;从数学编程基础、机器学习入门、深度学习深入到大模型探索&#xff0c;最后到进阶应用。每个阶段都包含理论学习和实践项目指导&#xff0c;并推荐了丰富的学习资源。文章还强调了社区参与和持续学习的重要性&#xff0c;帮助小白…

作者头像 李华