MySQL事务是InnoDB引擎支持的DML操作逻辑集合,必须全部成功或全部失败;默认autocommit=1导致转账可能数据不一致;需显式使用START TRANSACTION和COMMIT保障原子性。
MySQL事务 是一组 DML 操作(INSERT、UPDATE、DELETE)的逻辑集合,它必须全部成功,或全部失败——没有“一半生效”的中间状态。这是 InnoDB 存储引擎提供的核心能力,MyISAM 等引擎不支持事务,直接绕过这个机制。
你写两条 UPDATE 转账语句,没加任何事务控制,结果张三扣了钱、李四没到账——这不是 bug,是 MySQL 的默认行为在起作用:
autocommit 默认为 1,即每条 DML 语句单独成一个事务,自动提交UPDATE 执行完立刻落盘;第二条若因网络中断、程序崩溃、SQL 错误而失败,已提交的第一条无法撤回✅ 正确做法:显式关闭自动提交,用 START TRANSACTION 包裹操作:
SET autocommit = 0; START TRANSACTION; UPDATE account SET money = money - 100 WHERE name = '张三'; UPDATE account SET money = money + 100 WHERE name = '李四'; COMMIT;
⚠️ 注意:SET autocommit = 0 只对当前会话有效;生产环境更推荐在代码里用 BEGIN/COMMIT 显式控制,避免依赖会话级配置。
很多人以为“事务保证数据正确”,其实 consistency 不是数据库自动校验业务规则,而是指:事务前后,数据库必须满足你定义的约束(如主键、外键、CHECK、唯一索引等)。
money 字段加 CHECK(money >= 0),事务照样允许余额变成 -500 —— 这不算违反 ACID,只是你的业务一致性没落地COMMIT 成功 ≠ 业务正确;它只代表“数据库层面没违反约束”? 真正兜底要靠:约束定义 + 应用层校验 + 幂等设计(比如用转账单号防重)
MySQL 默认隔离级别是 REPEATABLE READ,它能防止脏读、不可重复读,但**无法完全避免幻读**(比如 SELECT ... FOR UPDATE 范围内新插入一行,再次查出来多了一条)。
READ COMMITTED:每次 SELECT 都读最新已提交版本 → 可能不可重复读,但幻读概率更低REPEATABLE READ:基于 MVCC 快照读,可重复读,但快照不覆盖新插入行 → 幻读仍可能发生SERIALIZABLE:加范围锁强制串行,性能差,一般不用? 实际选择建议:
REPEATABLE READ,幻读场景手动加 SELECT ... FOR UPDATE 或用唯一业务键替代范围查询version 字段)或分布式事务框架(如 Seata)事务不是开了就一直挂着,它会在以下任一情况自动终止:
COMMIT 或 ROLLBACK 显式结束DDL(如 ALTER TABLE)、DCL(如 GRANT)——这些语句会隐式提交当前事务exit)→ 同样自动回滚⚠️ 关键陷阱:应用层长事务(比如导出报表+更新状态)若没设好连接超时,可能卡住锁、拖垮并发。务必在代码里明确 COMMIT 或 ROLLBACK,不要依赖自动清理。