← 每日面试题 学习看板

什么是死锁?如何避免和解决

面试题/03数据库中间件/01MySQL/05事务与锁机制/什么是死锁?如何避免和解决.md

面试记录 2026-09-17

题目:什么是死锁?如何避免和解决

难度:low

标签:db/mysql/transaction, mysql/deadlock, difficulty/low

评分:⭐⭐⭐☆(3/5)

我的回答

说一下什么是死锁。死锁是多个线程对一组资源的竞争,然后大家相互拿到了不同的资源,又互不相互相不能释放,然后大家都在等待其他的线程释放资源,最终导致整个程序的锁死,各个资源都无法进行下一步工作。举个例子,例如一组资源为 1234,这时候线程 A 拿到了资源 1,线程 B 拿到了资源 2,线程 C 拿到了资源 3,这时 A 想要拿资源 2 的时候,发现资源 2 被 B 拿了,A 无法访问,这时候 B 想要拿资源 1 的时候,发现资源被 A 拿了。也无法访问,相互就锁死了。如果大家没有等待的条件,大家都会等到整个线程崩溃。但是如果想要破坏这个死锁,就需要从这几个条件中去排除这个问题。首先是资源的分组性,就是资源的分散性,大家没有一次性拿到全部资源。第二是资源的不可争抢性,就是我拿了资源,我不能抢别人资源。第三,长时间等待没有释放。第四,互斥。这几个性质其实破坏掉了,整个死锁也就不存在了。

解除死锁的话,第一个,如果我要申请一个一组资源,一次申请一组资源,整个资源拿到了我才去执行,这样就不会跟其他人有争抢,也就不会有死锁。第二,如果资源我长时间申请不到,这时候我就放弃当前执行,资源放给其他人,这个事也能继续做。第三,如果我拿到了资源 1,但是我没拿到资源 2,我有强权去把别人的资源 2 拿过来,这时候这个事也能通过。解决死锁的几个这个几个方向。

点评

优点:

不足:

补充:

完整讲解

第一部分:知识重点剖析

核心考点定位:既要有操作系统层死锁的理论理解,更要掌握 InnoDB 死锁的检测、监控与业务侧规避手段。

知识点拆解:

  1. 死锁理论(四必要条件):
  1. InnoDB 的死锁机制:
  1. InnoDB 常见死锁场景:
  1. 死锁监控与排查:
  1. 业务侧防死锁方案(优先级从高到低):
  1. 参数调整场景:

第二部分:考察方向与延伸递进

考察方向:

延伸递进面试题:

  1. 为什么两个相同 SQL 并发执行会死锁,同一个 SQL 单独跑不会?
  1. innodb_deadlock_detect=OFF 有什么后果?
  1. 怎么在压测环境主动触发死锁?

第三部分:面试者参考回答(口语化)

面试官您好,死锁我先从理论上讲清楚,再落到 InnoDB 的具体表现。

理论上死锁有四个必要条件:互斥、占有且等待、不可剥夺、循环等待。破坏任意一个就能防止死锁。经典例子是两个事务,A 拿了资源 1 等 2,B 拿了资源 2 等 1,就形成循环等待,谁都动不了。

但在 MySQL InnoDB 里,死锁的表现和检测方式跟理论不太一样,我主要讲几个点。

InnoDB 的默认行为是自动检测。innodb_deadlock_detect 默认开启,事务在等锁的时候会持续构建 wait-for graph,一旦发现环,就立刻选出牺牲者——通常是 undo log 少、改动少的那个事务,回滚它并抛 ERROR 1213: Deadlock found,另一事务继续执行。所以开发者看到的错误是 1213,业务代码要做重试。检测本身有 O(N²) 开销,事务特别多的场景可以考虑关闭检测,改用 innodb_lock_wait_timeout(默认 50 秒)来兜底——超时会抛 1205 错误。

常见死锁场景有几个。第一是间隙锁交叉,RR 隔离级别下两个事务用不同的 WHERE 条件触发不同间隙锁,最终循环等待。第二是多行更新顺序不同,A 先锁 id=1 再锁 id=2,B 反过来。第三是 INSERT ... ON DUPLICATE KEY UPDATE,并发插相同主键时一个走 INSERT 一个走 UPDATE 触发死锁。第四是 UPDATE 没写 WHERE 条件锁全表。第五是索引缺失导致锁大量行放大冲突面。

排查线上死锁我会按这个链路走:先跑 SHOW ENGINE INNODB STATUS 看 LATEST DETECTED DEADLOCK 段,里面会显示两个事务的 SQL、各自持有的锁、等待的锁;再看 performance_schema.data_lock_waits 找锁等待链;最后看 innodb_trx 找超长事务。慢查询日志里频繁出现 1213 错误就是死锁高发信号。

预防方案我按优先级列一下。第一是固定加锁顺序,所有事务按主键升序加锁,这是最有效的办法,业务侧只要约定统一顺序就能消掉大部分死锁。第二是缩小事务粒度,一条 SQL 能完成就别拆多条,事务内减少锁数量。第三是避免长事务,长事务持有锁越久,越容易和其他事务撞。第四是加索引,无索引的 UPDATE 会锁大量行。第五是用乐观锁绕过,version 列 CAS 更新,冲突就重试,不用悲观锁串行化。第六是8.0 的 NOWAIT / SKIP LOCKED,SELECT FOR UPDATE NOWAIT 拿不到锁立即返回,避免阻塞等待成环。

参数层面,默认配置基本够用,只是长事务多、检测开销大的场景可以 innodb_deadlock_detect=OFF,靠 innodb_lock_wait_timeout 兜底。反过来热点行死锁频发的场景,把 innodb_lock_wait_timeout 调小让失败快一点,业务侧快速重试。

https://chat.deepseek.com/share/wy9ppt0dw6b7uhgyct


面试记录 2026-10-13(第 2 次)

题目:什么是死锁?如何避免和解决

难度:low

标签:db/mysql/transaction, mysql/deadlock, difficulty/low

评分:⭐⭐☆☆☆(2/5)

练习次数:2

我的回答

死锁就是一个彻底解不开的锁,表示这个动作被卡顿停止,没有人工介入是无法解除的。

构成死锁核心有这么几个原因。首先是资源没有一次性申请,不同的线程获取了不同的资源,又相互造成循环等待。

最后我申请资源,你也申请资源,大家申请的,你申请的是我的,我申请的是你的,我不能抢你的,你也不能抢我的,这里边就涉及一个不能抢占其他人资源,也是导致死锁的一个核心原因。

总结一下死锁构成的原因有,没有一次性申请所有资源,相互等待了资源,资源不能强制争抢。这 3 个任意破坏一个都可以解除这个死锁。

点评

优点:

不足:

补充:

ℹ️ 完整讲解见上方首次记录(2026-09-17)