锁类型总览
InnoDB 的锁按粒度分成几层:行级锁包含记录锁、间隙锁与临键锁,表级锁包含意向锁与自增锁,另外还有一类元数据锁作用在表结构上。业务里感知到的锁等待绝大多数来自行锁与元数据锁。更关键的一点是:行锁锁的并不是物理行,而是索引记录——条件列没有索引时,扫描到的所有记录都会被锁住,看起来就像锁了整张表。
基本信息
| 行级锁 | 记录锁、间隙锁、临键锁 |
|---|---|
| 表级锁 | 意向锁、自增锁 |
| 元数据锁 | 作用在表结构上,改表与数据访问互相阻塞 |
| 默认隔离级别 | 可重复读下会使用间隙锁与临键锁 |
| 加锁对象 | 索引记录,而不是物理行 |
| 查看手段 | 锁信息视图、锁等待视图、引擎状态输出 |
典型使用场景
- 并发写入互相阻塞,确认是不是锁范围过大
- 在线改表被阻塞,确认是否存在长时间未提交的事务
- 批量更新锁住大量行,评估是否需要补索引
- 死锁复盘:确认事务的加锁顺序
- 巡检输出锁等待与长事务清单
查看锁现状
查看锁现状
SELECT * FROM performance_schema.data_locks\G # 当前持有的锁明细
SELECT * FROM sys.innodb_lock_waits\G # 等待关系与阻塞方
SHOW ENGINE INNODB STATUS\G # 查看事务段与最近的死锁信息锁类型速查
| 锁 | 粒度 | 触发条件 |
|---|---|---|
| 记录锁 | 行 | 等值条件命中索引记录 |
| 间隙锁 | 索引区间 | 可重复读下的范围条件 |
| 临键锁 | 记录加间隙 | 可重复读下的默认加锁方式 |
| 意向锁 | 表 | 行锁存在时自动加,不阻塞读取 |
| 自增锁 | 表 | 自增列的并发插入 |
| 元数据锁 | 表结构 | 改表与数据访问互相阻塞 |
条件列没有索引时会退化成扫描全表并锁住大量记录:锁问题的根因常常是索引缺失,而不是并发太高。
元数据锁容易被忽略:一个长事务没有提交,改表语句就会一直排队,表现为改表卡住而不是数据行锁等待。
减少锁冲突清单
- 条件列补索引,缩小加锁范围
- 事务尽量短,避免长事务持有锁
- 批量更新分批提交,降低单次锁范围
- 统一业务侧的加锁顺序,减少死锁
- 监控锁等待时长并配置告警
死锁由数据库自动检测并回滚其中一个事务:看到报错不用慌,要做的是顺着两个事务的加锁顺序去调整业务逻辑,让它按同一顺序访问资源。