MySQL原子性唯一依赖undo log实现,BEGIN/COMMIT仅为事务开关;TRUNCATE不记undo log故不可回滚,DROP TABLE在8.0+因原子DDL才支持回滚;REPEATABLE READ靠MVCC快照读实现,但UPDATE需当前读;COMMIT成功仅表示redo log刷盘,数据页可能未落盘;一致性需ACID三性与应用层校验共同保障。

ACID不是MySQL自动“开箱即用”的魔法,而是InnoDB靠四套日志和锁机制协同工作的结果——缺一不可,且每项都有明确的失效边界。
undo log才是原子性的唯一凭据,BEGIN/COMMIT只是开关
很多人误以为写了BEGIN和COMMIT就天然具备原子性。实际上,InnoDB只在修改数据前把旧值写进undo log,才真正为回滚留了退路。
- 事务失败或执行
ROLLBACK时,InnoDB直接用undo log里的旧值覆盖Buffer Pool中的脏页 -
TRUNCATE TABLE不记undo log,所以无法回滚;DROP TABLE在MySQL 8.0+才因原子DDL支持回滚 - 事务提交后,
undo log不会立刻删除——只要还有其他事务依赖它做MVCC快照,就得一直保留
REPEATABLE READ不是“锁全表”,而是靠MVCC + 一致性视图
InnoDB默认隔离级别REPEATABLE READ下,第一次SELECT会拍一个快照,后续读都基于这个快照,但UPDATE或DELETE仍要查最新已提交版本——否则无法判断行是否被别的事务改过。
- 这就是为什么会出现“快照读”看到旧值,而“当前读”(如
SELECT ... FOR UPDATE)看到新值 - 想强制读最新已提交数据,必须显式加锁:
SELECT ... LOCK IN SHARE MODE或SELECT ... FOR UPDATE - 间隙锁(Gap Lock)和临键锁(Next-Key Lock)是可重复读下防止幻读的关键,但它们只在
WHERE条件命中索引时生效
COMMIT成功 ≠ 数据已落盘,redo log刷盘才是持久性底线
执行COMMIT返回成功,只代表redo log已按innodb_flush_log_at_trx_commit配置刷到磁盘,不代表对应的数据页已写入磁盘。
- 设为
1:每次COMMIT都强制刷redo log到磁盘,崩溃后可完整恢复 - 设为
0或2:redo log可能只写入OS缓存,断电可能丢失最近1秒内提交的事务 -
redo log是物理日志,记录的是“某页某偏移改成什么值”,靠重放才能还原数据页——它不负责语义,只保物理变更可重建
一致性不是数据库兜底的,它依赖ACID三性+应用层校验共同达成
ACID里只有A、I、D是InnoDB能保证的机制,C(Consistency)是业务目标,不是技术实现。
- 外键约束、唯一索引这些靠InnoDB检查,但余额是否为负、库存是否超卖,得靠应用层逻辑或触发器
- 即使所有ACID都满足,如果没加
UNIQUE索引防重复下单,照样产生脏数据 - 真正的风险点常在DDL混入事务:比如在事务里执行
ALTER TABLE,会隐式触发COMMIT,导致前面的DML提前提交,破坏原子边界


















