MySQL锁
MySQL 锁机制的核心是"锁粒度 + 锁模式 + 锁算法"三个维度,事务并发安全(事务 的隔离)最终都靠锁落地。锁粒度决定锁住多少数据(行/表/间隙),锁模式决定读写互斥关系(共享/排他),锁算法决定如何锁区间(记录锁/间隙锁/Next-Key Lock)。理解三者组合,才能解释"为什么 RR 隔离级别下不会幻读""为什么死锁发生了"。
锁粒度
| 粒度 | 锁什么 | 特点 | 使用场景 |
|---|---|---|---|
| 表锁(Table Lock) | 整张表 | 开销小、并发差 | MyISAM 全表锁;DDL 变更 |
| 行锁(Row Lock) | 单行记录 | 开销大、并发高 | InnoDB 默认 |
| 页锁(Page Lock) | 一页数据 | 介于两者之间 | 早期 BDB 引擎 |
InnoDB 的行锁是建立在索引上的:没有走索引的 UPDATE/DELETE 会升级为锁全表(逐行扫描加锁),这是生产事故的高频来源——更新大表时务必确认条件走索引。
锁模式
| 模式 | 兼容性 | 含义 |
|---|---|---|
| 共享锁(S 锁,读锁) | S 与 S 兼容;S 与 X 互斥 | 只读当前行,阻止其他事务修改 |
| 排他锁(X 锁,写锁) | 与所有锁互斥 | 修改当前行,阻止其他事务读写 |
| 意向共享锁(IS) | 表级标记 | 事务准备给某行加 S 锁,先声明"我可能读表" |
| 意向排他锁(IX) | 表级标记 | 事务准备给某行加 X 锁,先声明"我可能写表" |
意向锁的作用
意向锁解决"行锁与表锁的冲突判断效率"问题:没有意向锁时,加表锁要逐行检查是否有行锁;有了意向锁,表级 X 锁直接与意向锁冲突判断即可。意向锁只表达意图,不阻塞任何行级操作。
SELECT * FROM t WHERE id = 1 FOR SHARE; -- 加共享锁(8.0 语法)
SELECT * FROM t WHERE id = 1 FOR UPDATE; -- 加排他锁锁算法(InnoDB 如何锁区间)
InnoDB 的锁按"是否锁住数据两侧的间隙"分三种,这是幻读问题的核心:
| 算法 | 锁什么 | 目的 |
|---|---|---|
| 记录锁(Record Lock) | 单条索引记录 | 防止该行被修改/删除 |
| 间隙锁(Gap Lock) | 两条记录之间的区间 | 防止区间内插入新记录 |
| Next-Key Lock | 记录 + 其前面区间 | 记录锁与间隙锁的组合,防幻读 |
间隙锁的代价
间隙锁锁住的是"区间"而非具体行,会阻塞区间内任何新插入,高并发插入场景(如秒杀扣减库存区间)可能因间隙锁互相等待形成死锁。RR 隔离级别下默认使用 Next-Key Lock;RC 级别只用记录锁(但会产生幻读,见 事务)。
幻读如何被阻止
RR 级别下,事务内 SELECT ... WHERE id > 100 FOR UPDATE 不仅锁住已存在的行,还用 Next-Key Lock 锁住 id > 100 的整个区间——另一个事务想插入 id=101 的记录会被阻塞,从而阻止幻读。
死锁
死锁是多个事务互相持有对方需要的锁、循环等待导致的。InnoDB 通过等待图(wait-for graph)检测发现死锁,自动回滚代价最小的事务(innodb_deadlock_detect 默认开启)。
| 常见成因 | 示例 | 对策 |
|---|---|---|
| 加锁顺序不一致 | 事务 A 先锁行 1 再锁行 2;事务 B 先锁行 2 再锁行 1 | 统一加锁顺序(如按主键升序) |
| 间隙锁互等 | 两个事务同时插入相邻区间 | 缩小锁范围、调整隔离级别 |
| 大事务持锁过久 | 一个事务锁多行未提交,其他事务排队 | 缩短事务、批量操作分小批 |
死锁检测的开关权衡
高并发下死锁检测本身有性能开销(每个新锁请求都要构建等待图)。确定不会死锁的场景(如单行更新)可关闭检测(innodb_deadlock_detect=OFF)并依赖锁等待超时兜底(innodb_lock_wait_timeout 默认 50 秒)。
SHOW ENGINE INNODB STATUS; -- 查看最近一次死锁详情(LATEST DETECTED DEADLOCK 段)排查与常见问题
| 问题 | 现象 | 处理 |
|---|---|---|
| 锁等待超时 | Lock wait timeout exceeded | SHOW PROCESSLIST 找阻塞源,检查长事务 |
| 行锁升级表锁 | 大表更新突然变慢 | 确认 WHERE 走索引 |
| 死锁 | Deadlock found | 看 SHOW ENGINE INNODB STATUS 定位循环等待,统一加锁顺序 |