InnoDB通过redo log、undo log、行级锁与MVCC协同实现ACID:redo log保障持久性,undo log支撑原子性与一致性,行级锁+MVCC保障隔离性;redo与binlog通过两阶段提交协调;undo log记录旧版本供回滚和MVCC读取;next-key lock(行锁+间隙锁)防止幻读,唯一索引等值查询存在时才优化为record lock。

事务的ACID特性在InnoDB中靠什么落地
InnoDB不是靠“声明式支持”来提供事务,而是用一套协同工作的底层机制硬实现的:redo log 保证持久性(D),undo log 保证原子性(A)和一致性(C),而行级锁 + MVCC 一起支撑隔离性(I)。这四者缺一不可,删掉任意一个,START TRANSACTION 就只剩个空壳。
redo log 和 binlog 的分工与协作
很多人误以为 commit 成功 = 数据已刷盘。实际是:事务提交时,InnoDB 只确保 redo log 写入磁盘(由 innodb_flush_log_at_trx_commit 控制),而数据页(ibd 文件)可能还在 buffer pool 中延迟写入。这样既快又安全——崩溃后可用 redo log 重放已提交但未落盘的变更。
binlog 是 Server 层日志,不参与崩溃恢复,只用于主从复制和归档。MySQL 用两阶段提交(2PC)协调二者:prepare 阶段写 redo log 并标记为 prepared;commit 阶段先写 binlog,再将 redo log 标记为 commit。任一环节失败,crash recovery 都能靠这个状态判断是否回滚。
undo log 如何支撑回滚和 MVCC
每次 UPDATE 或 DELETE,InnoDB 不直接覆盖旧数据,而是把原记录写进 undo log,并用 roll_pointer 指向它。这样:
- 执行
ROLLBACK时,顺着roll_pointer链一路恢复即可 - 其他事务做
SELECT时,根据自己的read view判断该读哪个版本——这就是 MVCC 的核心。注意:READ COMMITTED每条语句都生成新read view,而REPEATABLE READ在事务第一次SELECT时就固定住read view -
purge线程异步清理已无事务需要的 undo log,否则ibdata1会持续膨胀
行锁、间隙锁与死锁检测的真实开销
InnoDB 默认加的是 next-key lock(行锁 + 间隙锁),不是单纯的“某一行被锁”。比如 WHERE id = 5 在唯一索引上才可能是纯行锁;若查的是非唯一索引或范围条件(如 WHERE age BETWEEN 20 AND 30),就会锁住索引间隙,防止幻读——这也意味着并发插入可能被意外阻塞。
死锁检测由 innodb_deadlock_detect 控制,默认开启。一旦触发,InnoDB 会主动回滚代价最小的事务(按 undo log 大小估算)。但高并发下频繁检测本身有 CPU 开销;关掉它虽省资源,却要靠 innodb_lock_wait_timeout 超时被动退出,响应更不可控。
真正容易被忽略的是:唯一索引等值查询(=)且记录存在时,InnoDB 才会优化为仅加 record lock;只要记录不存在,哪怕条件唯一,也会退化为 gap lock —— 这点在设计防重逻辑时经常踩坑。










