首页/数据/SQL 速查/事务基础:BEGIN / COMMIT / ROLLBACK
变更与事务52 条

事务基础:BEGIN / COMMIT / ROLLBACK

把多条语句打包成原子操作:MySQL 用 START TRANSACTION 或 BEGIN,PostgreSQL 用 BEGIN;提交用 COMMIT,撤销用 ROLLBACK。

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

事务是什么

事务把多条语句打包成一个原子操作,要么全部生效,要么全部不生效。MySQL 里用 START TRANSACTION 或 BEGIN 开始,PostgreSQL 用 BEGIN;提交用 COMMIT,撤销用 ROLLBACK。InnoDB 与 PostgreSQL 默认都开启自动提交,单条语句自成一个事务,只有显式开始事务后多条语句才成组。工程上最关注的是原子性与隔离性:前者保证不出现一半成功一半失败,后者决定并发事务之间能看到什么。而长事务是所有数据库都讨厌的东西,它会长时间持有锁与版本信息。

基本信息

作用把多条语句打包为原子操作,整体提交或整体回滚
MySQL 开始START TRANSACTION 或 BEGIN,需要 InnoDB 引擎
PostgreSQL 开始BEGIN 或 START TRANSACTION
提交与回滚COMMIT 提交,ROLLBACK 撤销
自动提交默认 autocommit 打开,单条语句自成一个事务
保存点SAVEPOINT 与 ROLLBACK TO 可用于局部回滚

典型使用场景

  • 转账与扣减库存:两处变更必须同时成功或同时失败
  • 订单主表加明细:一组写入包在一个事务里,避免主表有记录而明细缺失
  • 批次清理:删除与归档成对执行,中途失败整体回滚
  • 批量导入:多条写语句攒够一次提交,吞吐明显高于逐条提交
  • 配合锁读实现先查后改:SELECT … FOR UPDATE 必须在事务内才有意义
  • 数据修复:变更前先开事务试跑,确认结果再提交

基本写法

基本写法
-- MySQL 与 PostgreSQL 通用写法
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;   # 转出
UPDATE accounts SET balance = balance + 100 WHERE id = 2;   # 转入
-- 两处余额都改对了再提交,否则整体撤销
COMMIT;          -- 出错时执行 ROLLBACK;

-- 希望部分回滚时用保存点
SAVEPOINT sp1;
UPDATE accounts SET balance = balance - 1 WHERE id = 3;
ROLLBACK TO SAVEPOINT sp1;   # 只撤销保存点之后的操作

关键语句对照

语句MySQLPostgreSQL
开始事务START TRANSACTION / BEGINBEGIN / START TRANSACTION
提交COMMITCOMMIT
回滚ROLLBACKROLLBACK
保存点SAVEPOINT 与 ROLLBACK TOSAVEPOINT 与 ROLLBACK TO
自动提交开关SET autocommit = 0SET autocommit = off
只读事务START TRANSACTION READ ONLYBEGIN READ ONLY
MySQL 里执行 DDL 会隐式提交当前事务,PostgreSQL 支持在事务里执行 DDL;把建表改表与数据变更混在一个事务里时要特别小心。
事务里不要夹网络调用或大批量计算:长事务会长期占用 undo 与锁资源,还会阻塞 DDL 与后台清理,表现为磁盘膨胀和并发变慢。
同一个业务操作必须在同一个数据库连接里完成:连接池分配到不同连接就是不同事务,锁与快照都不再共享。
MySQL 的 MyISAM 引擎不支持事务与行锁,只有 InnoDB 才具备事务能力;换引擎或做表结构变更前先确认这一点。

事务与并发实践

  • 事务粒度尽量小:只包必要的写语句,把查询与外部调用挪到事务外
  • 更新顺序保持一致:多个事务按同样顺序改同一批行,能明显减少死锁
  • 捕获死锁并重试:数据库会牺牲其中一个事务(MySQL 1213、PostgreSQL 40P01),应用层要有重试逻辑
  • 批量写入一次提交:几千行一次 COMMIT 吞吐远高于逐行提交,但要控制单批大小
  • 只读事务可以显式声明:部分数据库允许只读事务以减少一致性维护开销
  • 事务对象不要跨请求复用:事务的生命周期必须与数据库连接绑定
  • 监控长事务:定期查当前运行的事务,超过阈值就告警排查
事务与连接池要成对设计:从池里借出连接后开启事务,提交或回滚之后再归还;中途归还连接会让事务悬空,产生难以复现的数据问题。

易错点

  • 转账类操作忘记放在同一事务:一侧提交、一侧失败会产生资金不平,业务写操作必须成组提交
  • 事务里混入耗时操作(HTTP 调用、大批量计算)会长时间持锁,应把网络调用移到事务外
  • 忘记 COMMIT 就断开连接,事务被回滚,表现为数据莫名没写进去
  • 在 MySQL 事务里执行 DDL 触发隐式提交,前面的操作已落库却以为可以一起回滚
  • 把自动提交关闭后忘记恢复,后续语句一直悬在未提交事务里

数据来源与参考

  • MySQL 8.0 Reference Manual · START TRANSACTION
  • PostgreSQL Documentation · BEGIN
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌