MySQL事务不是“有事务功能”就完事了,它必须满足
ACID四个硬性约束才算真正可用。不支持 ACID 的所谓“事务”,比如
MyISAM引擎的
BEGIN/COMMIT,只是语法上像,实际没原子性、没回滚、没隔离——等于裸奔。
原子性(Atomicity):要么全成,要么白干,中间状态不能留
这是事务最基础的底线。比如转账:扣 A 账户、加 B 账户,这两步必须捆在一起。如果扣款成功但加款失败,数据库绝不能只留下“A 少了钱、B 没多钱”的残缺状态。
靠undo log实现:每条
UPDATE/INSERT/DELETE都先记下“怎么反向撤销”,出错时直接按 log 倒带
START TRANSACTION后,任何语句报错(如主键冲突、字段超长、违反
CHECK约束),只要没
COMMIT,整个事务自动标记为需回滚 常见坑:
INSERT IGNORE或
ON DUPLICATE KEY UPDATE看似“容错”,但它们属于单条语句级处理,不改变事务整体原子性逻辑;真要保原子,得靠外层事务 + 异常捕获 注意:只有
InnoDB支持完整原子性;
MyISAM不写
undo log,
ROLLBACK无效
一致性(Consistency):不是数据库自己“觉得一致”,而是你定义的规则必须被强制执行
一致性不是事务“带来”的,而是事务 + 约束 + 触发器 + 应用逻辑共同守住的红线。事务只是那个“守门人”:它确保约束检查发生在事务提交前,且失败即回滚。
典型体现:FOREIGN KEY级联、
CHECK(balance >= 0)、唯一索引冲突、触发器抛异常——这些都会让
COMMIT失败 容易忽略的点:一致性是**业务语义层面**的。比如银行总余额不变,这不是数据库自动保证的,而是靠你在事务里显式校验(如
SELECT SUM(balance)前后比对)或靠应用逻辑兜底
SET FOREIGN_KEY_CHECKS = 0会绕过外键检查,等于主动放弃一部分一致性保障,上线前务必确认是否真需要
隔离性(Isolation):并发时别互相“串味”,但 MySQL 默认不完全隔离
InnoDB默认隔离级别是
REPEATABLE READ,但它**不等于**串行执行。幻读(Phantom Read)在当前读(
SELECT ... FOR UPDATE)下仍可能发生,只是快照读(普通
SELECT)被 MVCC 隐藏了。 四个级别差异关键在“读可见性”:
READ UNCOMMITTED可见未提交修改(脏读);
READ COMMITTED每次
SELECT都读最新已提交版本(不可重复读);
REPEATABLE READ事务内所有快照读都基于首次读的时间点;
SERIALIZABLE加范围锁,彻底串行 实战建议:高并发写场景慎用
SERIALIZABLE,性能损耗大;
REPEATABLE READ下若需防幻读,必须显式加
SELECT ... FOR UPDATE或用
INSERT ... ON DUPLICATE KEY UPDATE替代“先查再插” 一个经典陷阱:
UPDATE t SET x = x + 1 WHERE id = 1在
READ COMMITTED和
REPEATABLE READ下行为一致,但
UPDATE ... WHERE name = 'xxx'(无索引)可能锁全表,导致严重阻塞
持久性(Durability):提交即落地,断电也不能丢
一旦
COMMIT返回成功,数据就算服务器立刻断电、进程崩溃,重启后也必须可恢复——这靠的是
redo log刷盘机制,不是直接写数据文件。
innodb_flush_log_at_trx_commit是核心开关:
1(默认)= 每次提交都刷盘,最安全;
0= 每秒刷一次,崩溃最多丢 1 秒事务;
2= 写 OS 缓存但不强制刷盘,依赖 OS 稳定性 别被“磁盘写入”误导:
redo log是顺序 I/O,远快于随机写数据页;所以即使
sync_binlog=1开启,只要
innodb_flush_log_at_trx_commit=1,持久性就有保障 真实风险点:某些云厂商 RDS 默认调低该参数以换性能,或使用机械盘 + 高并发写导致
fsync成瓶颈,此时
COMMIT延迟飙升,应用可能超时误判失败
ACID 不是抽象概念,每个字母背后都是日志策略、锁机制、约束检查和刷盘控制的具体实现。真正踩过
undo log被提前 purge 导致回滚失败、
redo log空间满引发 hang、或者
REPEATABLE READ下幻读引发重复插入的坑,才会明白——事务的“可靠”,从来不是开个
BEGIN就自动附送的。
