
事务必须显式开启,不能依赖自动提交
MySQL 默认是 autocommit=1,每条 UPDATE 或 DELETE 都会立刻落盘,根本没进事务。PostgreSQL 和 SQLite 也类似——除非你明确执行了 BEGIN(或 START TRANSACTION),否则所谓“在事务里”只是幻觉。
常见错误现象:UPDATE 执行成功了,但后续 INSERT 失败,前面的修改却没回滚。
- Python(sqlite3):调用
conn.execute("BEGIN"),别只靠conn.isolation_level = None的默认行为 - Java(JDBC):设
connection.setAutoCommit(false),且必须在所有 SQL 前设置 - Node.js(pg):用
client.query("BEGIN")+client.query("COMMIT")/client.query("ROLLBACK"),别用pool.query()直接发修改语句
所有相关操作必须在同一个数据库连接内完成
事务是连接级上下文,跨连接就失效。Web 应用里最容易踩这个坑:中间件、ORM、日志模块各自拿一个连接去查/改,看着像在“一个事务里”,其实全是独立事务。
使用场景:比如扣库存 + 记订单 + 发通知,如果通知服务走另一个 HTTP 调用再连 DB,那它那条 INSERT 就完全不受主事务控制。
- ORM 如 Django:用
transaction.atomic()确保整个代码块共用同一连接 - Go(sqlx):传入同一个
*sql.Tx对象给所有tx.Exec(),而不是用db.Exec() - PHP(PDO):
$pdo->beginTransaction()后,所有操作都调$pdo->prepare()+$stmt->execute(),别混用新PDO实例
ROLLBACK 不会自动触发,出错后必须手动执行
很多语言的数据库驱动不会在异常时自动回滚——它只抛错,连接还开着,事务状态仍是 “active”。如果你没捕获异常并显式 ROLLBACK,连接可能被放回连接池,下次复用时发现事务还在挂着,导致锁表或数据不一致。
错误信息示例:ERROR: current transaction is aborted, commands ignored until end of transaction block(PostgreSQL)
- Python:用
try...except...finally,finally里判断conn.in_transaction再决定是否rollback() - Node.js:Promise 链中用
.catch()显式client.query("ROLLBACK"),别只throw - 超时也要防:设置
SET statement_timeout = '30s'(PostgreSQL)或应用层加 timeout,避免长事务卡死
读操作也受事务隔离级别影响,别默认用 READ COMMITTED
你以为 SELECT 只是读,不影响一致性?错。在 READ COMMITTED 下,两次 SELECT 可能读到不同结果(不可重复读),导致基于第一次查询结果做的 UPDATE 逻辑错乱。比如先查余额是 100,再扣 50,但中间别人已转走 80,实际只剩 20。
性能影响:提高隔离级别(如 REPEATABLE READ 或 SERIALIZABLE)会增加锁竞争和回滚概率,不是越高越好。
- MySQL InnoDB:默认
REPEATABLE READ,对大多数资金类操作够用;真要强一致,用SELECT ... FOR UPDATE显式加行锁 - PostgreSQL:默认
READ COMMITTED,需在事务开头执行SET TRANSACTION ISOLATION LEVEL REPEATABLE READ - 注意:SQLite 的
Serializable是通过文件锁模拟的,高并发下易阻塞,别在写密集场景硬上
BEGIN,而是你确认了连接没换、异常有兜底、读写逻辑在同一个隔离视图里跑完——这些地方一松动,一致性就漏了。










