当前位置: 首页 > news >正文

如何避免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.大批量要分批:别一次锁太多行。

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

http://www.jsqmd.com/news/101554/

相关文章:

  • C语言中实现Modbus转IEC 60870-5-103协议转换器
  • 如何快速上手VNote:从零开始的Markdown笔记体验
  • RocketMQ 高并发场景优化:消息压缩、批量发送与消费线程池调优
  • 5分钟掌握:安卓防撤回黑科技,从此不再错过任何重要信息
  • 11、Vim 文件操作与移动技巧全解析
  • allegro工艺边的制作和mark点放置
  • 12、Vim高效操作:文件内导航技巧
  • arcpy导出excel表
  • LobeChat能否支持Web Components?组件化开发实践
  • 旅行攻略助手:LobeChat规划完美行程
  • 从 SEC 定调到资产上链,Synbo 正在搭建下一代金融秩序
  • 从理论到代码:手把手教你实现AI原生混合推理模型
  • Beyond Compare 5完整激活指南:从问题排查到成功授权
  • 【C语言手撕算法】LeetCode-142. 环形链表 II(C语言)
  • Transformers v5 升级来袭:简洁设计+无缝体验!
  • SQL Server 2025安装教程
  • 深入理解 C# 中 new 关键字的三重核心语义
  • 终极指南:如何轻松获取完整中国行政区划数据
  • RAG技术演进:从检索辅助到智能体,掌握大模型应用的关键技术!
  • MeshLab文件格式全攻略:从新手到高手的3D模型处理指南 [特殊字符]
  • 项目风险管理:LobeChat识别潜在威胁
  • LobeChat能否支持批量导入提示词?工作效率提升技巧
  • FGO终极自动战斗工具:2025年新手快速上手指南
  • 数组(练习)
  • LobeChat微信公众号文章开头生成技巧
  • 【ACM出版,稳定检索设计类】2026年人工智能与产品设计国际学术会议 (AIPD 2026)
  • BetterNCM插件管理器深度体验:让网易云音乐秒变全能播放器
  • LobeChat科研基金申请书撰写助手
  • 2026年程序员转型秘籍:掌握这三点,大模型算法工程师年薪翻倍不是梦!
  • 手握明星开源项目却不会赚钱?GOBI 2025 全球开源商业创新大会全日程发布,附参会指南!