隐式提交是什么
很多 DDL 语句无法回滚:MySQL 执行 CREATE / ALTER / DROP / TRUNCATE / RENAME、LOCK TABLES、GRANT 等语句时会隐式提交当前事务,前面未提交的写入被顺手落库,之后 ROLLBACK 也回不来。PostgreSQL 的 DDL 是事务型的、可以整体回滚,但仍有不能在事务块内执行的例外,典型是 CREATE INDEX CONCURRENTLY、VACUUM、ALTER SYSTEM。SQL Server 多数 DDL 可回滚。写迁移脚本时把 DDL 与 DML 混在一个事务里,是最容易出问题的地方。
基本信息
| MySQL 隐式提交 | DDL、CREATE INDEX、LOCK TABLES、GRANT、SET autocommit=1、ANALYZE / OPTIMIZE TABLE 等 |
|---|---|
| MySQL 例外 | CREATE / DROP TEMPORARY TABLE 不触发隐式提交 |
| PostgreSQL | DDL 参与事务;CREATE INDEX CONCURRENTLY、CREATE DATABASE、VACUUM、ALTER SYSTEM 不能在事务块内执行 |
| SQL Server | 大部分 DDL 可回滚,少数语句需按版本查文档 |
| 影响 | 回滚范围被切断,事务只保护最后一段语句 |
典型使用场景
- 发版迁移脚本:把建表、建索引与初始化数据写在同一个文件里
- 灰度变更:先建新表,再拷数据,最后改名切换,需要分段控制失败边界
- 本地验证迁移:想整体试跑一遍再回滚,必须避开隐式提交语句
- 批处理脚本:在事务里做数据处理,中途插入清理临时表以外的大表变更
- 排查「回滚没生效」:确认中间是否混入了 DDL 或 LOCK TABLES
MySQL 演示
MySQL 演示
START TRANSACTION;
INSERT INTO orders (id, amount) VALUES (1, 100); # 这条还在事务里
ALTER TABLE orders ADD COLUMN note varchar(50); # DDL 触发隐式提交
ROLLBACK; # 前面的 INSERT 已落库,无法撤销
# 临时表是例外:不触发隐式提交
CREATE TEMPORARY TABLE tmp_ids (id bigint); # 当前事务保持有效
INSERT INTO tmp_ids VALUES (1);
ROLLBACK; # 这次连临时表数据也能回滚PostgreSQL 对照
PostgreSQL 对照
BEGIN;
CREATE TABLE t_new (id bigint); -- PG 的 DDL 参与事务,可以回滚
INSERT INTO t_new VALUES (1);
ROLLBACK; -- 表与数据一起消失
BEGIN;
CREATE INDEX CONCURRENTLY idx_big ON big_table (col); -- 报错:不能在事务块中执行
COMMIT;
-- 需要并发建索引时,脚本应单独执行该语句,不套 BEGIN平台差异
| 平台 | DDL 可否回滚 | 例外与提示 |
|---|---|---|
| MySQL 8.0 | 多数 DDL 隐式提交,不可回滚 | CREATE / DROP TEMPORARY TABLE 不提交 |
| PostgreSQL | 可以,DDL 事务型 | CONCURRENTLY / VACUUM / ALTER SYSTEM 不能在事务块内 |
| SQL Server | 多数可回滚 | 少数语句有例外,按版本确认 |
| 通用做法 | DDL 与 DML 分开执行 | 每段独立控制成功与失败边界 |
迁移脚本务必把 DDL 与 DML 拆开执行:一旦在事务中间混入 DDL,后面的失败回滚只能撤销最后一段,库里会留下半成品状态,需要人工比对修复。
MySQL 默认 autocommit=1,每条语句自成事务;显式 BEGIN 只是暂时关闭自动提交,任何 DDL 或 SET autocommit=1 都会把它恢复为已提交状态。