MySQL事务的四个核心特性到底指什么
MySQL事务的ACID不是抽象概念,而是InnoDB引擎在崩溃恢复、并发读写、异常中断等真实场景下必须守住的四条底线。它们各自解决一个具体问题:
原子性(Atomicity):保证“全成功或全失败”。比如转账时扣款和存款必须绑定,不能只执行一半。 一致性(Consistency):不是数据库自动保证的“业务正确”,而是靠原子性+隔离性+持久性+约束(如外键、CHECK)共同支撑的最终结果。例如转账前后总金额不变,是代码逻辑+事务机制+约束校验一起达成的。 隔离性(Isolation):解决并发读写冲突。默认REPEATABLE READ级别下,同一个事务内多次
SELECT看到的数据版本一致,靠MVCC + undo log实现;而写操作则依赖行锁、间隙锁防止幻读。 持久性(Durability):只要
COMMIT返回成功,即使立刻断电,重启后数据也不会丢——这靠的是
redo log刷盘(由
innodb_flush_log_at_trx_commit=1保障)。
undo log和redo log分别管什么,为什么不能少
很多人混淆这两个日志,其实分工非常明确:
undo log是“回滚凭证”:记录修改前的旧值(比如
UPDATE前的整行数据),用于
ROLLBACK或MVCC构造历史版本。它存在系统表空间里,事务提交后不会立刻删除,要等Purge线程异步清理。
redo log是“重做凭证”:记录物理页修改(比如“第5号页偏移量128处写入0x3A”),只管“怎么把内存里的脏页变化安全落地”。它不负责回滚,也不参与MVCC,只服务崩溃恢复。 两者缺一不可:没有
undo log,事务失败就无法回退;没有
redo log,Buffer Pool里的修改一旦宕机就全丢,
COMMIT就成空话。
为什么SET autocommit=0后,DDL语句还会自动提交
这是InnoDB的硬性规则,不是bug。所有DDL操作(如
CREATE TABLE、
ALTER TABLE、
DROP INDEX)都会隐式触发
COMMIT,哪怕你已经
START TRANSACTION了。 原因在于DDL会重建表结构或元数据,涉及存储引擎层状态变更,无法被普通事务包裹。 后果很实际:如果你在事务中先
UPDATE再
ALTER TABLE,前面的
UPDATE会立刻生效,无法通过后续
ROLLBACK撤销。 规避方法只有两个:把DDL单独放在独立连接里执行,或用
pt-online-schema-change等工具做无锁变更。
隔离级别选错,查不到数据却以为是BUG
最常见踩坑是误以为
READ COMMITTED比
REPEATABLE READ“更安全”,结果发现同一事务内两次
SELECT结果不同,怀疑数据丢了。
REPEATABLE READ(InnoDB默认):事务启动时生成一个全局
Read View,后续所有
SELECT都基于这个快照,所以不会不可重复读。
READ COMMITTED:每次
SELECT都生成新
Read View,能看到其他事务刚
COMMIT的数据——这正是“不可重复读”的定义,但它是符合标准的正常行为。 真正要注意的是:如果业务依赖“事务内读一致性”(比如先查余额再扣款),就不能随意切到
READ COMMITTED,否则可能多扣或少扣。 事务的复杂点不在语法,而在日志生命周期、锁范围、MVCC快照时机这些底层协同细节。比如
undo log没被Purge完,会导致
history list堆积,拖慢查询;又比如
gap lock只在
REPEATABLE READ下生效,换到
READ COMMITTED后幻读风险就回来了——这些都不是调个参数就能解决的,得看懂它在做什么。
