mysql事务由存储引擎实现,innodb支持完整acid,myisam静默忽略;原子性依赖undo log前镜像回滚;一致性需原子性、隔离性、持久性与应用逻辑协同;持久性取决于redo log落盘及fsync实效。

事务不是“功能开关”,而是存储引擎的行为契约
MySQL 本身不直接提供事务,真正实现 ACID 的是底层存储引擎。InnoDB 支持完整事务,MyISAM 完全不支持——哪怕你写了 BEGIN 和 COMMIT,MyISAM 也会静默忽略,不报错也不回滚。所以第一步永远是确认引擎:SHOW CREATE TABLE t1 看 ENGINE=InnoDB;如果用的是云数据库或 ORM,默认建表可能仍是 MyISAM(尤其老项目迁移时),这会导致“事务看似生效,实际没隔离、没原子性”的幻觉。
原子性靠 undo log,但回滚不是“撤销 SQL”,而是重放日志
很多人以为 ROLLBACK 是把刚执行的 SQL 反向执行一遍,比如 INSERT 就 DELETE,UPDATE 就 SET 回旧值。其实 InnoDB 是靠 undo log 中记录的“前镜像”(before image)来恢复数据页状态。这意味着:
-
INSERT失败后回滚,不只是删行,还会释放自增 ID、清理二级索引条目、还原聚簇索引 B+ 树结构 - 大事务(比如批量更新百万行)会生成巨量 undo log,填满
ibdata1或独立 undo 表空间,导致后续事务卡住甚至失败:ERROR 1205 (40001): Deadlock found when trying to get lock或更隐蔽的Lock wait timeout exceeded -
autocommit=0下手动开启事务后,忘了COMMIT或ROLLBACK,连接空闲挂着,undo log 一直不能清理,长期会拖慢整个实例
一致性不是数据库“自动保证”,而是 ACID 各环节协同的结果
一致性(Consistency)常被误解为“数据库自己校验约束”。其实它没有独立实现机制,而是原子性 + 隔离性 + 持久性 + 应用层逻辑共同达成的状态结果。比如转账:
- 原子性确保扣款和入账不拆开
- 隔离性(如
REPEATABLE READ)防止中间态被其他事务读到(避免“读到 A 少了但 B 还没多”的脏读) - 外键约束、CHECK、触发器等只是辅助手段;若业务逻辑绕过这些(比如用
INSERT ... ON DUPLICATE KEY UPDATE替代显式事务),一致性就完全依赖代码正确性 - InnoDB 不强制校验应用层语义一致性:账户余额不能为负,这个规则得靠
CHECK(8.0.16+)或应用层判断,否则UPDATE account SET balance = balance - 1000 WHERE id = 1即使余额只剩 10 块,SQL 也照样执行成功
持久性不等于“写磁盘完成”,而是 redo log 落盘 + fsync 成功
COMMIT 返回成功,只代表 redo log 已刷入磁盘(由 innodb_flush_log_at_trx_commit 控制),不代表对应的数据页已落盘。常见配置:
-
innodb_flush_log_at_trx_commit = 1(默认):每次COMMIT都触发fsync(),最安全,但 IO 开销大 -
= 0:log 写入内存缓冲区即返回,崩溃可能丢最近一秒事务 -
= 2:log 写入 OS 缓存即返回,崩溃不丢事务,但 OS 崩溃仍可能丢失
注意:即使设为 1,若磁盘本身是带写缓存但未配电池/电容的 RAID 卡,fsync 也可能被硬件缓存拦截,看起来成功实则未落盘——这是线上故障中极难排查的一类“伪持久”问题。
ACID 四个特性里,隔离性和一致性最容易被当成黑盒依赖,但它们的实际行为高度依赖配置、引擎版本、甚至硬件栈。调参不是贴膏药,得清楚每一处妥协换来了什么,又放弃了什么。











