AI帮我们修改订单、支付、库存、积分这类业务时,经常会遇到一种很麻烦的问题:
代码没有明显报错,业务却只成功了一半。
比如:
- 订单创建成功了,但库存没扣;
- 用户状态已经更新,积分却没有增加;
- 数据库写入成功,消息通知没有发出去;
- 支付记录保存了,后续服务调用却失败;
- 本地测试正常,线上偶尔出现数据不一致。
这类问题表面看像某一步“漏执行了”。
但真正的核心通常是:
整个业务流程并不处在同一个事务边界里。
一、数据库事务只能保护它能控制的部分
假设一次下单包含:
创建订单 → 扣库存 → 写支付记录
如果这三步都在同一个数据库、同一个事务中执行,那么其中任何一步失败,都可以统一回滚。
最终结果要么:
全部成功
要么:
全部失败。
但真实系统往往没有这么简单。
订单可能在订单库,库存属于另一个服务,消息还要发送到MQ。
这时候一个数据库事务就无法覆盖整条业务链。
二、最容易出现问题的是“数据库已经提交,外部调用才失败”
例如流程是:
写订单 → Commit → 调用库存服务
数据库已经提交成功。
随后库存服务超时。
这时即使代码捕获异常,也无法简单把已经提交的数据库事务重新回滚。
于是系统变成:
订单存在,但库存没有变化。
这就是典型的“只成功一半”。
所以排查事务问题时,不能只看:
有没有 transaction。
更应该看:
事务到底覆盖了哪些步骤。
三、AI很容易把每一步都“处理正确”,却忽略整体原子性
AI修改代码时,通常会分别处理:
- 数据库异常;
- HTTP调用异常;
- MQ发送异常;
- 超时;
- 重试。
单独看每一步都很合理。
但如果业务真正要求的是:
订单、库存、积分必须保持一致。
那么局部异常处理再完善,也不代表整个流程具有原子性。
这就是账号3这类问题最典型的特点:
每段代码都对,组合起来却可能不对。
四、异常被Catch以后,事务甚至可能继续提交
还有一种更隐蔽的情况。
例如事务内部某一步失败以后,被代码捕获:
记录日志,然后继续执行。
如果异常没有继续抛出,事务框架可能认为:
整个方法正常结束。
最终照常提交。
于是部分数据已经写入,失败步骤却没有执行成功。
所以看到事务里存在宽泛的 catch 时,要特别检查:
异常到底是为了处理,还是被直接“吃掉”了。
如果业务要求整体失败,异常就不能被无条件吞掉。
五、跨服务调用不能指望数据库Rollback解决
例如:
订单服务 → 库存服务 → 优惠券服务 → 消息服务
这些步骤很可能分布在不同系统。
订单服务自己的数据库事务,只能控制自己的数据库。
它不能直接让另一个服务:
把刚才扣掉的库存一起Rollback。
所以跨服务场景真正需要解决的是:
失败以后怎么恢复一致。
常见思路包括:
- 补偿操作;
- Outbox;
- 事件驱动;
- 状态机;
- 最终一致性;
- 可重试任务。
核心不再是“所有服务同时回滚”,而是:
即使中途失败,系统也有办法把状态最终修正回来。
六、为什么简单重试也可能让问题更复杂?
假设第一次调用库存服务其实已经成功,只是响应超时。
订单服务认为失败,于是再次重试。
如果库存扣减没有幂等保护,就可能:
订单只有一笔,库存却扣了两次。
所以事务、一致性和Retry往往不能分开看。
跨服务操作至少要同时考虑:
失败补偿 + 幂等性。
否则为了修复“一半成功”,又可能制造“重复成功”。
七、Outbox解决的是哪类问题?
一个常见问题是:
数据库已经提交,但消息发送失败。
例如订单已经创建成功,随后要发一条:
OrderCreated
消息。
如果直接:
写数据库 → 发MQ
中间就存在断点。
Outbox思路通常是:
业务数据和待发送事件先在同一个数据库事务里一起保存。
事务成功后,再由后台任务把事件可靠发送出去。
这样至少可以避免:
数据已经成功,但系统完全忘记还有一条消息需要发送。
它解决的不是“所有系统强制同步提交”,而是提高跨系统最终一致性的可靠性。
八、排查“只成功一半”时,可以先画出完整执行链
不要先盯着某一段事务代码。
先把业务过程完整列出来:
请求进入 → 写订单 → 扣库存 → 写积分 → 发消息 → 返回结果
然后给每一步标记:
- 属于哪个数据库;
- 属于哪个服务;
- 有没有事务;
- 什么时候Commit;
- 失败后能不能回滚;
- 不能回滚时有没有补偿;
- 重试时是否幂等。
一旦把这些边界画清楚,通常很快就能看到:
哪一步已经离开了原来的事务保护范围。
九、测试也不能只测“全部成功”
普通测试经常只验证:
正常下单是否成功。
但事务一致性真正应该重点测试的是失败路径。
例如:
- 写订单后库存调用失败;
- 库存成功后消息发送失败;
- Commit前抛异常;
- Commit后外部接口超时;
- 同一个请求重复执行;
- 补偿任务执行失败。
真正危险的问题,大多藏在:
第一步成功、第二步失败
这种中间状态里。
十、可以让AI专门做一次“事务边界检查”
以后让AI修改这类代码时,可以明确要求:
请列出完整业务执行链,标记每一步属于哪个事务和服务。检查数据库Commit发生的位置、异常是否会触发Rollback、外部API和MQ是否位于数据库事务之外,以及中途失败后是否存在补偿、Outbox或幂等机制。不要只验证全部成功场景,请补充中间步骤失败的测试。
这样比简单要求:
把这段代码加事务。
更接近真实工程问题。
最后
AI改完事务逻辑后出现“只成功一半”,本质上通常不是:
事务没有写。
而是:
事务边界比业务边界更小。
数据库可以回滚自己的修改,但它无法自动回滚已经调用成功的其他服务,也无法保证消息、库存、支付等多个系统同时成功。
所以真正需要关注的是:
哪里提交、哪里不能回滚、失败以后怎么补偿,以及重试会不会重复执行。
当一项业务跨越数据库、服务和消息系统以后,真正的正确性就不再只是:
每一步有没有成功。
而是:
部分失败以后,整个系统最终还能不能回到正确状态。
持续更新 Codex、AI编程与后端工程实战内容,更多深度内容和稳定订阅渠道欢迎搜索关注「孤狼GPT」。



