MySQL 中的
BEGIN、
COMMIT和
ROLLBACK是手动控制事务的核心语句,但它们**不保证自动开启事务**——是否真正进入事务状态,取决于当前会话的
autocommit设置。
autocommit 关闭才是事务生效的前提
MySQL 默认开启
autocommit=1,此时每条 DML 语句(如
INSERT、
UPDATE)都会立即提交,
BEGIN后执行的语句仍可能被自动提交,导致
ROLLBACK失效。 必须先执行
SET autocommit = 0,再用
BEGIN(或
START TRANSACTION)显式开启事务
BEGIN本身不是 SQL 标准关键字,在 MySQL 中是
START TRANSACTION的同义词,但不带任何隐式设置作用 某些客户端(如 phpMyAdmin、MySQL Workbench)可能默认关闭
autocommit,但脚本或连接池中往往不会,不能依赖
COMMIT 和 ROLLBACK 的边界很明确
事务从
BEGIN(或
START TRANSACTION)开始,到
COMMIT或
ROLLBACK结束。之后的语句不属于该事务,也不会被回滚。
COMMIT后,所有修改永久写入磁盘(受存储引擎和刷盘策略影响),无法撤销
ROLLBACK仅能回滚当前未提交事务中的修改;如果中间执行过
COMMIT,之前已提交的部分不可逆 执行
ROLLBACK后,事务结束,后续语句需重新
BEGIN才能再次开启事务
嵌套事务在 MySQL 中不存在
MySQL 不支持真正的嵌套事务。
BEGIN后再写一个
BEGIN,不会创建子事务,而是被忽略或报错(取决于版本和 SQL 模式)。 想实现“部分回滚”,只能靠
SAVEPOINT:例如
SAVEPOINT sp1、
ROLLBACK TO sp1
SAVEPOINT不会结束事务,
ROLLBACK TO后仍可继续
COMMIT或
ROLLBACK注意
SAVEPOINT名称不能重复,否则后一次会覆盖前一次
常见误用场景与示例
下面这段代码在
autocommit=1下运行,
ROLLBACK实际无效:
SET autocommit = 1;
BEGIN;
INSERT INTO users(name) VALUES('Alice');
ROLLBACK;正确写法应为:
SET autocommit = 0;
BEGIN;
INSERT INTO users(name) VALUES('Alice');
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;或者更稳妥地,用标准语法并显式控制:
SET autocommit = 0;
START TRANSACTION;
INSERT INTO users(name) VALUES('Bob');
DELETE FROM logs WHERE created_at < NOW() - INTERVAL 7 DAY;
COMMIT;最容易被忽略的是:应用层连接(如 Python 的
pymysql、Java 的
Connection)通常默认开启
autocommit,且事务状态不跨请求保持——每次 HTTP 请求都得自己重置
autocommit并管理
BEGIN/COMMIT/ROLLBACK。
