mysql事务是以start transaction为起点、commit或rollback为终点的执行契约,仅对innodb等支持事务的引擎生效,解决状态可靠性问题而非语法问题。

MySQL 事务不是“功能开关”,而是一套**以 START TRANSACTION 为起点、以 COMMIT 或 ROLLBACK 为终点的执行契约**——它只对支持事务的存储引擎(如 InnoDB)生效,MyISAM 等引擎完全无视事务语句。
事务本质:为什么不能靠“写两条 UPDATE 就完事”?
转账场景最典型:A 减 100、B 加 100。如果中间断电或程序崩溃,只执行了第一条,数据库就永远不一致了。事务解决的不是“语法问题”,而是“状态可靠性问题”——它让 MySQL 能在出错时倒带回原始状态。
-
autocommit=1(默认)下,每条 SQL 都是独立事务,START TRANSACTION才真正开启多语句协同控制 - 事务不是锁表工具,但会隐式加行级锁(
InnoDB),并发高时可能引发Lock wait timeout exceeded - DDL 语句(如
ALTER TABLE)会自动触发隐式提交,导致当前事务提前结束
ACID 四特性里,最容易被误解的是“一致性”
很多人以为“一致性”等于“数据正确”,其实它更接近“约束守门员”:只要违反了主键、外键、CHECK、非空等约束,事务就会被强制回滚——哪怕你没写 ROLLBACK。
- 原子性靠
undo log实现:记录反向操作(比如减 100 的 undo 是“加 100”) - 持久性靠
redo log保证:即使断电,已COMMIT的修改也能从日志恢复 - 隔离性不是“绝对隔离”,而是通过
MVCC + 锁实现读写不互斥,但不同隔离级别表现差异极大
四个隔离级别怎么选?看业务容忍度,别盲目调高
REPEATABLE READ(InnoDB 默认)不是银弹。它能防不可重复读,但幻读仍存在(比如 SELECT ... FOR UPDATE 后插入新行)。真要杜绝幻读,得用 SERIALIZABLE,但代价是所有写操作串行化,QPS 断崖下跌。
-
READ COMMITTED:适合高并发读多写少场景(如订单查询),每次SELECT都读最新已提交版本 -
REPEATABLE READ:适合需要多次读取同一快照的场景(如报表生成),但需配合SELECT ... FOR UPDATE或间隙锁防幻读 - 临时改级别用
SET TRANSACTION ISOLATION LEVEL xxx,仅对当前会话生效
显式事务的坑:SAVEPOINT 不是万能回滚点
SAVEPOINT 只能回滚到该点之后的操作,且不能跨事务生命周期。更关键的是:它不释放锁——如果在保存点前已锁住某行,回滚到该点后,那行依然被锁着,其他事务照样阻塞。
- 正确用法:
START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; SAVEPOINT sp1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 若第二步失败,可回滚到 sp1,保留第一步结果 ROLLBACK TO sp1;
- 常见误用:在循环中反复
SAVEPOINT却不RELEASE SAVEPOINT,导致内存泄漏(尤其长事务) -
ROLLBACK会清除所有保存点;COMMIT也会自动清空
事务不是越长越好,也不是越多越好。一个事务里混进慢查询、大范围扫描或外部 API 调用,轻则锁表,重则拖垮整个库。真实系统里,90% 的事务应该控制在 100ms 内完成,且只包裹真正需要原子性的那几条语句。










