首页/数据/SQL 速查/隐式提交:DDL 与事务的边界
变更与事务52 条

隐式提交:DDL 与事务的边界

MySQL 里 DDL 语句会隐式提交当前事务;PostgreSQL 的 DDL 是事务性的、可以回滚,但持有更强的锁。

SQL 速查数据更新于 2026-10-13

隐式提交是什么

很多 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 不触发隐式提交
PostgreSQLDDL 参与事务;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 都会把它恢复为已提交状态。

易错点

  • 以为 ROLLBACK 能撤销全部改动,混入 DDL 后前面写入已提交,回滚只覆盖最后一段
  • 在事务里对大表执行 ALTER TABLE,事务被隐式提交后无法整体回滚,出问题只能再写逆向 DDL
  • 把 CREATE INDEX CONCURRENTLY 放进 BEGIN … COMMIT,PostgreSQL 直接报错并中断整个脚本
  • 误以为 CREATE TEMPORARY TABLE 也会隐式提交,白白拆分脚本、牺牲事务一致性

数据来源与参考

  • MySQL 8.0 Reference Manual · Statements That Cause an Implicit Commit
  • PostgreSQL Documentation · Transactional DDL
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌