sql事务应短、准、可控:仅包裹强一致性操作,移出异步/日志等非核心逻辑;分批提交、用savepoint局部回滚;合理选隔离级别,慎用分布式事务;禁事务内调外部服务;加强监控诊断。

SQL 事务处理在复杂业务场景中容易成为性能瓶颈和数据一致性风险点。关键不是“加事务”本身,而是让事务足够短、足够精准、足够可控。
缩小事务边界:只包裹真正需要原子性的操作
常见误区是把整个 HTTP 请求或 Service 方法全包进一个事务里,比如用户下单时顺带更新积分、发消息、写日志——这些非核心步骤不该拖慢订单主流程。事务应严格限定在“扣库存 + 创建订单 + 写支付流水”这类必须全部成功或全部回滚的操作范围内。
- 把异步通知、日志记录、缓存刷新等非一致性强依赖操作移出事务体,改用事件驱动或补偿机制
- 对批量操作拆分为小批次提交(如每次 100 条),避免长事务锁表或触发数据库自动回滚(如 MySQL 的 innodb_lock_wait_timeout)
- 用 SAVEPOINT 实现局部回滚,例如在循环插入中某条失败时仅回退当前轮次,不影响前面已成功数据
合理选择隔离级别:不盲目追求 SERIALIZABLE
高隔离级别带来强一致性,也带来高锁竞争和性能下降。大多数业务场景下 READ COMMITTED 已足够,配合应用层校验即可规避脏读与不可重复读的实质影响。
- 电商秒杀需防超卖,可用 SELECT ... FOR UPDATE 在 RC 级别下显式加行锁,比直接升到 RR 更轻量
- 报表类查询尽量走只读事务(SET TRANSACTION READ ONLY),让数据库优化执行路径,减少锁开销
- 跨库/微服务事务慎用分布式事务框架(如 Seata),优先考虑基于消息的最终一致性,降低系统耦合与故障传播面
避免事务内调用外部服务或耗时逻辑
数据库连接持有期间若等待 HTTP 响应、文件 IO 或远程 RPC,会持续占用连接池资源并延长锁持有时间,极易引发连接耗尽或死锁。
- 将外部调用(如调用微信支付 API、推送站内信)放在事务提交后,通过监听事务事件(如 Spring 的 @TransactionalEventListener)触发
- 如必须同步获取外部状态(如验证第三方账户余额),先查缓存或本地影子表,再异步对账,不阻塞主事务
- 禁止在事务中执行 ORDER BY RAND()、全表 COUNT 或未走索引的模糊查询,这些会放大锁范围与执行时间
监控与诊断:让事务行为可观察
生产环境事务问题往往滞后暴露。需主动埋点和采集关键指标,而非等报警才介入。
- 记录每个事务的开始时间、结束时间、SQL 执行数、扫描行数、是否发生回滚,接入 APM(如 SkyWalking)做聚合分析
- 定期检查 information_schema.INNODB_TRX 表,识别运行超 5 秒的活跃事务,定位长事务源头
- 开启慢查询日志并过滤含 BEGIN/COMMIT/ROLLBACK 的语句,快速发现隐式事务或未关闭连接的代码路径
事务不是银弹,而是要权衡一致性、性能与可维护性的精细操作。设计时多问一句:这一步真的需要和前面的操作“一起成功或一起失败”吗?答案往往是否定的。










