Java 后端面试里,事务和接口稳定性经常一起出现。面试官可能不会直接问“Spring 事务有哪些传播行为”,而是换成更贴近项目的问题:下单接口里哪些步骤要放进事务?调用第三方失败要不要回滚?接口超时后用户重复提交怎么办?你怎么证明这个接口上线后是稳定的?
这些问题看起来分散,其实都在考同一个能力:你能不能把代码里的一个方法,理解成一个有边界、有失败路径、有恢复手段的业务接口。
事务先讲边界,而不是先背注解
很多候选人一说事务就开始背 `@Transactional`。更好的回答是先讲业务边界。比如下单接口里,创建订单、扣减库存、写支付单,哪些必须一起成功,哪些可以异步处理,哪些失败后可以补偿。只有先把边界讲清楚,事务注解才有意义。
一个常见误区是把所有动作都放进同一个事务里。这样看似安全,实际可能让事务变长,锁持有时间变长,还会把外部接口的不稳定带进数据库事务。面试里可以明确说:数据库内强相关的状态变更放在事务里,通知、日志、消息发送、第三方调用尽量不要直接阻塞事务。确实需要联动时,用本地记录加后续补偿保证最终一致。
回滚不是“抛异常就行”
Spring 事务面试里,回滚规则很容易被追问。更重要的是,不要只说概念,要说你在项目里怎么避免误用。
比如业务异常被捕获后吞掉,事务可能不会回滚;同一个类里方法内部调用,事务代理可能不生效;异步线程里的数据库操作不在原事务里;先调用外部服务再回滚本地事务,外部动作不会自动撤销。这些都是真实项目里会出问题的点。
更成熟的表达是:我会让事务方法保持边界清晰,避免在事务里做太多外部调用;需要回滚的异常不随便吞掉;对外部动作采用业务状态机和补偿任务,而不是幻想数据库回滚能回滚一切。
接口稳定性从重复请求讲起
接口稳定性不是只看能不能跑通。面试官经常会问:用户点了两次提交怎么办?客户端超时但服务端成功了怎么办?支付回调重复来了怎么办?
回答这类问题,要主动讲幂等。幂等的意思是同一个业务请求重复执行多次,最终业务结果仍然只发生一次。常见做法包括:请求号唯一约束、业务单号去重、状态机限制流转、支付流水号校验、数据库唯一索引兜底。不要只说“前端按钮置灰”,因为前端控制挡不住网络重试、接口重放和第三方重复回调。
超时和重试要有上限
重试是稳定性手段,也可能制造事故。面试里不要笼统说“失败就重试”。要讲清楚什么错误可以重试、重试几次、间隔多久、是否会造成重复写入、失败后怎么告警。
比如查询类接口可以短暂重试,创建订单这类写接口必须依赖幂等键。调用库存、支付、风控等外部服务时,要设置超时时间和失败策略。服务降级也要分业务:推荐理由可以暂时不展示,支付状态不能随便猜,库存扣减失败不能假装成功。
一段可用于面试的项目表达
可以这样说:我负责的订单接口里,核心事务只覆盖订单创建、状态初始化和必要的库存校验,不把通知和统计写入放进主事务。为了避免重复提交,每次下单带业务请求号,订单表和幂等表都有唯一约束,重复请求会返回已有结果。支付回调按支付流水号去重,并通过订单状态机限制从待支付到已支付的流转。外部接口调用都有超时控制,失败后写补偿记录,由任务重试并记录最终结果。上线后看接口错误率、慢请求、重复提交拦截数量、补偿任务积压和关键状态不一致数量。
这比单纯背事务传播行为更有说服力。因为它回答的是面试官真正关心的问题:当接口遇到真实异常时,你设计的系统会怎样继续保持可控。
事务和接口稳定性的取舍
下单、支付、退款这类接口里,事务和稳定性经常交织在一起。面试时要避免把所有步骤都塞进事务,也要避免把所有失败都交给重试。
重复提交:事务负责是状态更新原子性,事务不负责是网络重试识别,还需要什么是幂等号和唯一约束。第三方超时:事务负责是本地记录一致,事务不负责是第三方最终状态,还需要什么是查询补偿和状态机。
- 回滚:事务负责是数据库内修改,事务不负责是已发出的外部消息,还需要什么是可靠消息或任务表。
- 接口慢:事务负责是缩短锁持有时间,事务不负责是下游不可控耗时,还需要什么是超时、降级、异步化。
更成熟的回答是:事务保证本地核心数据的一致性,接口稳定性还要靠幂等、超时、补偿和可观测性。两者不是一回事。