1. MySQL 的锁有哪些分类?

按粒度:

🎯 面试要点

  • 锁的兼容:S 与 S 兼容,S 与 X 互斥,X 与 X 互斥
  • 意向锁的意义:判断"能否加表锁"不用遍历所有行
  • 行锁是基于索引实现的:条件没走索引 → 行锁退化成表锁(全表加锁)——高频考点

2. 悲观锁和乐观锁?各自实现?

乐观锁扣库存(经典)
-- 表 stock(id, goods_id, count, version)

-- 1. 读版本号
SELECT count, version FROM stock WHERE goods_id = 100;  -- count=10, version=3

-- 2. 条件更新,version 校验
UPDATE stock
   SET count = count - 1, version = version + 1
 WHERE goods_id = 100 AND version = 3;     -- 影响 0 行 → 冲突,重试

🎯 面试要点

  • 乐观锁在高竞争下重试成本高;悲观锁在低竞争下锁开销浪费
  • update count = count - 1 WHERE count > 0 也是无锁扣减技巧(数据库行锁保证原子)
  • FOR UPDATE 要配合索引 + 事务;悲观锁在分布式下要换分布式锁(Redis)

3. 死锁如何产生?怎么排查和避免?

死锁:两个事务互相持有对方需要的锁,互不相让。InnoDB 自动检测死锁(等待图),选择回滚代价小的事务(undo 少者),并抛 Deadlock found when trying to get lock(1213 错误)。

排查:

避免:

  1. 所有事务按固定顺序访问资源(如先锁 id 小者)
  2. 尽量缩小事务范围(短事务少持锁)
  3. 避免锁升级为表锁(保证条件走索引)
  4. 业务层重试机制(死锁是常态,重试 2-3 次)

🎯 面试要点

  • MySQL 死锁会主动检测并牺牲一个事务,所以应用要能容忍 1213 错误重试
  • 死锁四条件(互斥/持有等待/不可剥夺/循环等待)——与 JVM 死锁同一套理论
  • 批量更新注意排序(ORDER BY id),避免不同事务更新顺序不一致

🎤 常见面试追问

  1. 行锁为什么会退化成表锁?——锁是加在索引上的:条件没走索引(全表扫)时,InnoDB 会锁住所有扫描到的行,实际效果等于表锁。所以"条件必须走索引"。
  2. 乐观锁和悲观锁怎么选?——写冲突频繁(库存扣减)→ 悲观锁(FOR UPDATE);写冲突少 → 乐观锁(version 字段,冲突重试)。
  3. MySQL 死锁了会怎样?怎么处理?——InnoDB 自动检测死锁并回滚"代价小"的事务(抛 1213 错误);应用要做死锁重试;预防:按固定顺序加锁、短事务、条件走索引。
  4. 间隙锁只有 RR 级别有吗?——是:间隙锁/临键锁只在 REPEATABLE READ 及以上生效;RC 下搜索与索引扫描不加间隙锁(例外:外键约束检查与唯一键重复检查仍会加间隙锁,官方文档明确列出)(所以 RC 并发更高、更少死锁)。
  5. 意向锁是干什么的?——加行锁前自动加的表级"意图标记"(IS/IX),让"判断能否加表锁"不用遍历所有行——表锁与行锁共存的协调机制。

📖 名词解释(本页术语)

术语 大白话解释
行锁只锁一行(InnoDB 特有),并发度高。加在索引记录上,条件不走索引会退化成表锁。
表锁锁整张表(MyISAM 只有表锁),实现简单但并发差。
共享锁(S)/ 排他锁(X)S 锁:可共享读,排斥写(LOCK IN SHARE MODE);X 锁:读写都排斥(UPDATE/DELETE/FOR UPDATE)。
意向锁(IS/IX)加行锁前自动加的表级标记,告诉别人"这表有行被锁了"——避免加表锁时扫全表判断。
记录锁 / 间隙锁 / 临键锁记录锁锁单行;间隙锁锁记录间的空隙(防插入);临键锁=两者结合(左开右闭),RR 防幻读主力。
悲观锁默认会冲突,先加锁再操作(SELECT ... FOR UPDATE)。
乐观锁默认不冲突,提交时校验(version 字段 + WHERE version=旧值),影响 0 行则重试。CAS 思想。
死锁两个事务互相持有对方要的锁。InnoDB 自动检测并牺牲一个事务(1213 错误),应用层要重试。
MDL 锁(元数据锁)所有访问该表的语句都会自动加:DML 取 MDL 读锁(共享),DDL 取 MDL 写锁(排他),用来保证表结构在语句执行期间不变。长事务持有的读锁只阻塞 DDL;后续 DML 被卡住是因为 DDL 已在等待队列排队,新来的 DML 申请读锁必须排在它后面(MDL 等待链会让整表不可用)。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。