事务默认在每条DML语句执行时瞬间开启并立即提交(autocommit=1),显式开启需SET autocommit=0或BEGIN/START TRANSACTION,实际事务活性始于第一条DML执行。

事务默认什么时候开启?别被“自动提交”骗了
MySQL 中事务不是你一连上就自动“开着”的,而是由 autocommit 状态决定的。默认情况下,autocommit=1,这意味着每条 INSERT、UPDATE、DELETE 语句都会被当作一个独立事务——执行完立刻提交,根本没机会回滚。
- 所以严格来说:**没有显式开启事务时,事务是“瞬间开启又瞬间结束”的**,你感觉不到它的存在
- 只有当你关闭自动提交(
SET autocommit = 0)或显式执行BEGIN/START TRANSACTION后,事务才真正“活”起来,进入可控制的生命周期 - 注意:
SELECT不会触发事务(除非加了FOR UPDATE或LOCK IN SHARE MODE),它只是快照读,不改变事务状态
显式开启事务的两种写法,效果一样但习惯不同
BEGIN 和 START TRANSACTION 都能开启一个新事务,它们在 InnoDB 中完全等价,没有底层差异,选哪个纯看团队规范或个人偏好。
-
BEGIN更简洁,适合快速交互式调试(比如 MySQL CLI 里随手测试) -
START TRANSACTION语义更清晰,明确表达“我要开始一个事务”,更适合生产 SQL 脚本或 ORM 日志中追踪 - ⚠️ 别用
BEGIN WORK—— 虽然语法合法,但已过时,官方文档不推荐
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
INSERT INTO log (msg) VALUES ('deducted 100');
COMMIT;事务真正的“起点”不在 BEGIN,而在第一条修改语句执行时
InnoDB 的事务生命周期其实比 SQL 语句更底层:当你执行 START TRANSACTION,MySQL 只是分配了事务 ID、初始化 undo log 空间;真正让事务“动起来”的,是第一条 DML(如 UPDATE)执行时——此时才会生成第一个 undo log 记录,并锁定对应行/间隙。
- 如果
START TRANSACTION后只执行SELECT,事务处于“空闲但活跃”状态,不产生 undo log,也不加锁(默认 RR 隔离级别下仍是快照读) - 一旦执行首个
UPDATE,InnoDB 才真正开始维护事务一致性,后续所有操作都受该事务上下文约束 - 这意味着:事务的“实际开启点”是数据变更动作,不是语法关键字——这对理解死锁和锁等待特别关键
容易被忽略的关键细节:BEGIN 之前的状态也影响事务行为
很多人以为 BEGIN 是事务一切的起点,其实不然。事务行为还取决于它之前的会话设置,尤其是隔离级别和 autocommit 状态。
-
SET TRANSACTION ISOLATION LEVEL READ COMMITTED必须在BEGIN之前执行才生效;如果写在BEGIN之后,会报错或被忽略(取决于 MySQL 版本) - 如果当前会话
autocommit=1,你执行BEGIN后没COMMIT就断开连接,MySQL 会自动回滚未提交的更改——但这个“自动回滚”不是事务本身的行为,而是连接终止时的清理机制 - 最隐蔽的坑:某些客户端(如 phpMyAdmin、DBeaver)会在每次执行 SQL 后自动加
COMMIT,即使你写了BEGIN,也可能被悄悄提交掉,务必检查客户端配置里的“自动提交”开关
事务真正可控的起点,是你关掉 autocommit 或敲下 BEGIN 的那一刻;但它的“呼吸感”——何时加锁、何时记日志、何时影响并发——全藏在第一条 DML 执行的瞬间。别只盯着语法,得看 InnoDB 实际做了什么。










