事务必须显式开启,BEGIN
或 START TRANSACTION
缺一不可
MySQL 默认是自动提交模式(
autocommit=1),每条 DML 语句单独成事务,执行完立刻生效。想把多条语句打包进一个事务,必须先显式开启——只写
COMMIT或
ROLLBACK没用,它们不会触发隐式开始。
常见错误:直接写
UPDATE t1 SET x=1; UPDATE t2 SET y=2; COMMIT;,结果两条更新各自提交,无法回滚。
BEGIN和
START TRANSACTION完全等价,任选其一即可 不推荐用
BEGIN WORK,虽兼容但冗余 开启后,后续所有 DML(
INSERT/
UPDATE/
DELETE)都暂存在当前事务中,直到
COMMIT或
ROLLBACK
SAVEPOINT
不是必需的,但能解决部分回滚需求
标准事务只有全提交或全回滚两种结局。如果中间某步出错,又不想放弃前面已成功的操作,就得靠
SAVEPOINT设立标记点。
例如:插入主表、插入从表、更新统计表 —— 若第三步失败,只回滚到第二步之后的状态,保留前两步。
定义:SAVEPOINT sp1;,名字可自定义,同一事务内可设多个 回滚到某点:
ROLLBACK TO SAVEPOINT sp1;(注意不是
ROLLBACK TO sp1) 释放某个保存点:
RELEASE SAVEPOINT sp1;,不影响事务本身 事务结束(
COMMIT或
ROLLBACK)会自动清除所有保存点
隐式提交语句会让事务意外终止
有些 SQL 语句执行时会强制提交当前事务,哪怕你没写
COMMIT,事务也立刻结束。这是最容易踩坑的地方,尤其在存储过程或混合 DDL/DML 场景中。
典型触发隐式提交的语句包括:
CREATE TABLE、
DROP TABLE、
ALTER TABLE、
TRUNCATE TABLE、
LOCK TABLES、
FLUSH LOGS等。 示例:
BEGIN; INSERT INTO t VALUES(1); CREATE TABLE tmp(id INT); INSERT INTO t VALUES(2); COMMIT;→ 实际只有最后一条
INSERT受事务保护
SELECT不会隐式提交,但
SELECT ... FOR UPDATE或
SELECT ... LOCK IN SHARE MODE属于事务内加锁操作,仍需在事务中使用 DDL 类操作尽量放在事务外,或确认是否真需要与 DML 绑定
事务隔离级别影响可见性,SET TRANSACTION ISOLATION LEVEL
必须在 BEGIN
前设置
MySQL 的默认隔离级别是
REPEATABLE READ,但如果你需要读已提交(
READ COMMITTED)来避免间隙锁或适配其他数据库行为,得手动改——而且时机很关键。
错误写法:
BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED;→ 此时事务已开启,该语句无效,级别仍是启动时的默认值。 正确方式:在
BEGIN前执行,如
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN;也可用会话级设置:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;,之后所有新事务都生效
READ UNCOMMITTED和
SERIALIZABLE同理,不能在事务中动态切换 事务的边界比看起来更“脆”:一条 DDL、一个连接中断、甚至某些客户端自动重连行为,都可能让
BEGIN形同虚设。实际写业务逻辑时,别只盯着
COMMIT写在哪,先确认事务是不是真的从你想的位置开始了。
