在关系型数据库(如 MySQL、PostgreSQL、Oracle 等)中,死锁(Deadlock)通常出现在多个事务并发访问相同资源并以不同顺序加锁时。虽然不能完全杜绝死锁,但可以通过设计和运维手段大幅降低发生概率,或在发生时快速检测与恢复。
下面从 设计、开发、数据库配置、运维 四个层面系统说明。
死锁最常见原因:加锁顺序不一致
❌ 错误示例:
✅ 正确做法:
统一规则:
先锁 users,再锁 orders
事务越长:
建议:
-- 不推荐
BEGIN;
UPDATE ...
CALL external_api();
COMMIT;
不要“用到才锁”,而是事务开始就锁定所有资源
-- 推荐
BEGIN;
SELECT * FROM account WHERE id=1 FOR UPDATE;
SELECT * FROM account WHERE id=2 FOR UPDATE;
-- 业务逻辑
COMMIT;
优先使用:
| 操作 | 锁类型 |
|---|---|
UPDATE WHERE PK= |
行锁 |
UPDATE 无索引 |
表锁 |
LOCK TABLE |
表锁 |
✅ 确保 UPDATE / DELETE 使用索引
-- 有索引 → 行锁
UPDATE user SET money=money-10 WHERE id=1;
某些数据库在以下情况会从行锁升级为表锁:
✅ 控制批量操作规模:
-- 分批
UPDATE orders SET status=1 WHERE id BETWEEN 1 AND 1000;
主流数据库都有死锁检测:
| 数据库 | 机制 |
|---|---|
| MySQL (InnoDB) | 自动检测并回滚代价小的事务 |
| PostgreSQL | 死锁超时(deadlock_timeout) |
| Oracle | 自动检测 |
✅ 不要试图“手动解决”,而是让数据库回滚
避免事务无限等待:
MySQL:
SET innodb_lock_wait_timeout = 5;
PostgreSQL:
deadlock_timeout = 1s
死锁是正常并发现象,应用必须能处理:
for (int i = 0; i < 3; i++) {
try {
executeTransaction();
break;
} catch (DeadlockException e) {
Thread.sleep(50);
}
}
✅ 重试是解决死锁的最后防线
| 隔离级别 | 死锁风险 |
|---|---|
| READ UNCOMMITTED | 低 |
| READ COMMITTED | 中 |
| REPEATABLE READ | 高 |
| SERIALIZABLE | 最高 |
✅ 一般用:
统一顺序锁资源
事务尽量短而小
索引必须走行锁
死锁靠重试兜底
如果你愿意,我可以:
只要告诉我你用的数据库和场景即可。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。