温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

数据库Deadlock死锁如何避免

发布时间:2026-10-09 02:29:51 来源:亿速云 阅读:105 作者:小樊 栏目:数据库

数据库死锁(Deadlock)是指两个或多个事务相互持有对方所需的锁,导致都无法继续执行。死锁无法 100% 彻底消除,但可以通过设计和运维手段大幅降低发生概率,并在发生后快速恢复。

下面从 原理 → 避免策略 → 实战建议 系统说明。


一、死锁产生的必要条件

死锁通常同时满足以下条件:

  1. 互斥:资源(如行锁)不可共享
  2. 持有并等待:事务持有一把锁,还在等另一把
  3. 不可剥夺:锁不能被强制释放
  4. 循环等待:A等B,B等A

避免死锁的核心思路:破坏以上至少一个条件。


二、常见避免死锁的方法

1️⃣ 统一访问顺序(最重要)

所有事务按相同顺序访问表和行

❌ 容易死锁:

事务A:update 用户 → update 订单
事务B:update 订单 → update 用户

✅ 避免方式:

统一:先用户,后订单

这是最有效、工程上最常用的方法


2️⃣ 缩短事务时间

事务越长,持锁越久,死锁概率越高。

✅ 建议:

  • 事务中 只放必要 SQL
  • 不要在事务中:
    • 调用远程接口
    • 做复杂计算
    • 等待用户输入

❌ 错误示例:

BEGIN;
UPDATE account SET balance=balance-100 WHERE id=1;
-- 等待第三方支付回调
UPDATE account SET balance=balance+100 WHERE id=2;
COMMIT;

3️⃣ 降低锁粒度

  • 尽量使用 行锁 而非表锁
  • 避免:
    LOCK TABLE
    SELECT ... FOR UPDATE(无索引)
    

✅ 确保 WHERE 条件走索引:

SELECT * FROM orders WHERE id = 1 FOR UPDATE;

4️⃣ 合理选择隔离级别

隔离级别 死锁风险
READ UNCOMMITTED 低
READ COMMITTED 中
REPEATABLE READ 高
SERIALIZABLE 最高

✅ MySQL 默认 RR,可评估是否降到 RC
✅ Oracle 默认 RC,死锁较少


5️⃣ 避免热点行竞争

典型场景:

  • 库存扣减
  • 账户余额
  • 计数器

✅ 优化方案:

  • 拆分热点(分桶计数)
  • 异步处理
  • 使用 CAS / 版本号
UPDATE stock
SET count = count - 1
WHERE id = 1 AND count > 0;

6️⃣ 使用锁等待超时 & 死锁检测

数据库自带机制:

  • MySQL:
    • innodb_lock_wait_timeout
    • 自动死锁检测并回滚代价小的事务
  • SQL Server:
    • 死锁监控 + 自动牺牲者(victim)

✅ 应用层要:

  • 捕获死锁异常
  • 重试(指数退避)

7️⃣ 应用层重试机制

死锁是“正常异常”,不是 bug。

for (int i = 0; i < 3; i++) {
    try {
        doTransaction();
        break;
    } catch (DeadlockException e) {
        sleep(50 * (i+1));
    }
}

三、死锁排查方法(实战)

MySQL

SHOW ENGINE INNODB STATUS;

重点看:

  • LATEST DETECTED DEADLOCK
  • 哪个事务持锁、等锁

SQL Server

SELECT * FROM sys.dm_tran_locks;

四、总结口诀 ✅

统一顺序访问,缩短事务时间
索引避免全表锁,热点拆分降竞争
死锁不是故障,重试即可恢复

如果你愿意,可以:

  • 告诉我你用的是 MySQL / Oracle / PG / SQL Server
  • 或贴一段 死锁日志 / SQL

我可以帮你做针对性死锁分析。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI
助
手