事务、锁与 MVCC
InnoDB 通过锁保证并发写的正确性,通过 MVCC 让快照读不加锁。理解二者,才能正确处理并发与死锁。
InnoDB 锁类型
加锁的本质是加在索引上:如果查询没有走索引,InnoDB 只能锁住更多行甚至全表。
快照读 vs 当前读
- 快照读:普通
SELECT,读 MVCC 版本链,不加锁。 - 当前读:
SELECT ... FOR UPDATE/LOCK IN SHARE MODE、UPDATE、DELETE、INSERT,加锁读取最新版本。
隔离级别下的加锁行为
以可重复读(RR)为例:
Tip
在 RC(读已提交)级别下没有间隙锁。若业务并发写冲突多、且不依赖 RR 的可重复读语义,将隔离级别改为 RC 能显著减少锁竞争与死锁概率,这是许多互联网公司的默认配置。
死锁
死锁是「互相等待对方持有的锁」。InnoDB 会自动检测并回滚代价较小的事务。
减少死锁的原则:
- 事务中访问多张表/多行时,按固定顺序加锁。
- 缩短事务、减少持锁时间,把非数据库操作移出事务。
- 尽量让加锁走唯一索引等值,缩小锁范围。
- 必要时设置锁等待超时与重试逻辑。
监控锁等待
Warning
长事务是锁问题的根源:它持锁久、undo 版本链长、还会拖慢 purging。生产上应监控并告警长事务(如 information_schema.innodb_trx 中 trx_started 距今过长的记录)。
小结
- 锁加在索引上:无索引条件会导致大范围加锁甚至全表锁。
- 快照读走 MVCC 不加锁;当前读与写操作会加锁。
- RR 有间隙锁、RC 没有;理解这点是减少死锁的关键。
- 固定加锁顺序、缩短事务、走唯一索引,是防死锁三件套。