必须显式开启事务(set autocommit=0 或 begin),隔离级别决定一致性边界(默认repeatable-read,金融场景可选serializable),rollback仅回滚数据库操作,无法撤销外部副作用,唯一约束和外键是保障一致性的关键。

事务必须显式开启,autocommit=0 是前提
MySQL 默认是自动提交模式(autocommit=1),每条 INSERT/UPDATE/DELETE 语句都会立即落盘,根本不会进入事务上下文。想用 ACID,第一件事就是关掉它:
SET autocommit = 0;或者更稳妥地用
BEGIN 或 START TRANSACTION 显式开启——这两者等价,且会自动把当前会话的 autocommit 临时设为 0。
常见错误:只写 COMMIT 却没配对 BEGIN,结果什么也没提交;或者在连接池场景下复用连接后忘记重置 autocommit 状态,导致后续语句意外被自动提交。
隔离级别决定“一致性”的实际边界
ACID 中的 “I”(Isolation)不是固定值,而是由 transaction_isolation 参数控制。MySQL 8.0+ 默认是 REPEATABLE-READ,但它仍可能产生幻读;若业务要求强一致性(比如金融扣款+记账必须看到完全一致的余额快照),得考虑 SERIALIZABLE ——但代价是锁范围扩大、并发下降。
关键点:
-
READ-COMMITTED能避免脏读,适合大多数 Web 应用,但同一事务内多次SELECT可能拿到不同结果 -
REPEATABLE-READ通过 MVCC 实现快照读,但SELECT ... FOR UPDATE会加间隙锁,防止幻读写入 - 修改隔离级别要明确作用域:
SET SESSION transaction_isolation = 'READ-COMMITTED';
ROLLBACK 不是万能兜底,得靠应用层逻辑补救
事务失败时 ROLLBACK 能回退已执行的 DML,但无法撤销已发生的外部副作用:比如发了短信、调了支付接口、写了 Kafka 消息。ACID 只管 MySQL 内部状态,不管跨系统一致性。
所以真实场景中:
- 必须在事务内完成所有数据库操作,再在事务外处理外部调用
- 如果外部调用失败,需要补偿逻辑(如反向退款),而不是指望
ROLLBACK - 避免在事务里做耗时操作(如 HTTP 请求),否则长事务会阻塞锁、拖垮性能
唯一约束和外键是 ACID 的“守门员”,别绕过它们
很多人为了“灵活性”在应用层做重复校验,却删掉数据库的 UNIQUE 约束或禁用 FOREIGN_KEY_CHECKS。这是在主动放弃 ACID 的 C(Consistency)保障。
真正该做的:
- 用
INSERT ... ON DUPLICATE KEY UPDATE替代先查后插,避免竞态 - 外键关联删除用
ON DELETE CASCADE,比应用层手动删更原子 - 遇到
ERROR 1062 (23000): Duplicate entry,说明约束起作用了——这不是 bug,是保护机制在报警
最易被忽略的是:在批量导入或迁移时临时关闭约束,事后忘了打开,导致后续数据静默失真。只要关过 FOREIGN_KEY_CHECKS,就必须确认它最终是 ON 状态。










