Skip to content

事务、MVCC 与锁

InnoDB 使用 MVCC 处理普通一致性读,使用锁协调写入、当前读和结构变更。MVCC 不等于没有锁:UPDATE、DELETE、SELECT ... FOR UPDATE、外键检查和 DDL 都可能等待锁。


隔离级别

隔离级别一致性读主要特点
Read Uncommitted可见未提交变化很少用于业务系统
Read Committed每条一致性读建立新 Read View减少部分 Gap Lock,同一事务可能不可重复读
Repeatable Read首次一致性读后通常复用 Read ViewInnoDB 默认;当前读仍使用锁
Serializable普通 SELECT 也可能按锁定读处理一致性强但并发成本高

快照读和当前读必须区分:普通 SELECT 通常读取 Read View;FOR UPDATEFOR SHARE 和 DML 读取当前版本并加锁。


MVCC

InnoDB 记录包含事务标识和指向 Undo 旧版本的回滚指针。Read View 根据活跃事务范围判断当前版本是否可见;不可见时沿 Undo Version Chain 查找合适版本。

长事务维持旧 Read View,会阻止 Purge 清理历史版本。事务即使没有持续执行 SQL,只要未提交仍可能影响整个实例。


锁类型

保护对象说明
Record Lock索引记录InnoDB 行锁实际锁索引项
Gap Lock索引记录之间的间隙阻止在间隙插入,不锁定已有记录本身
Next-Key LockRecord + 前方 GapRepeatable Read 下范围当前读的常见形式
Insert Intention准备插入的间隙位置不同位置插入可并发,但会与冲突 Gap Lock 等待
Intention Lock表级意向表明事务准备获取行级 S/X Lock
Auto-Increment Lock自增值分配行为受自增锁模式和批量写入影响
Metadata Lock表、Schema 等元数据DML 持有共享 MDL,DDL 需要不兼容的排他 MDL

间隙不是固定的“左开右闭业务区间”,而是索引顺序中的记录间空间。具体锁范围取决于访问索引、查询谓词、隔离级别、唯一性和最终执行计划。

唯一索引等值查询命中唯一记录时通常只需要 Record Lock;非唯一范围扫描可能锁定扫描到的记录和间隙。没有合适索引时,扫描范围和锁影响可能显著扩大。


MDL 与 DDL 阻塞

事务访问表后会持有 Metadata Lock 直到事务结束。一个长期未提交事务可能让 ALTER TABLE 等待;等待中的 DDL 又可能阻塞后续请求,形成连接堆积。

处理前应先查清阻塞链,而不是立即重启:

sql
SELECT * FROM performance_schema.metadata_locks;
SELECT * FROM performance_schema.data_lock_waits;
SELECT * FROM information_schema.innodb_trx;

死锁

死锁是事务形成循环等待,InnoDB 检测后选择一个事务回滚。应用应把死锁和 Lock Wait Timeout 视为可重试错误,并保证事务幂等。

降低死锁概率:

  • 多事务按相同顺序访问对象和行。
  • 保持事务短小,不在事务中等待外部接口或人工操作。
  • 为过滤条件建立正确索引,缩小扫描与加锁范围。
  • 批量修改分段执行,但保持明确一致性边界。
  • 保存 Deadlock Log 和完整 SQL/参数进行复盘。

参考资料