面试题答案
一键面试基于TCC模式的设计方案
- 设计方案
- Try阶段:对各个服务的资源进行预留。例如在一个订单创建及库存扣减的场景中,订单服务在Try阶段创建订单记录,但标记为未确认状态,库存服务在Try阶段检查库存并冻结相应库存数量。此阶段主要完成初步的业务检查和资源预留,要求具有幂等性,防止重复调用造成数据不一致。
- Confirm阶段:确认Try阶段的操作,真正提交事务。如果所有服务的Try阶段都成功,订单服务将订单状态更新为已确认,库存服务正式扣减冻结的库存。该阶段同样要求幂等性,因为可能由于网络等原因导致重复调用。
- Cancel阶段:若Try阶段有任何一个服务失败,所有已执行Try操作的服务需要执行Cancel操作,释放预留的资源。如订单服务删除未确认的订单记录,库存服务解冻冻结的库存。
- 可能遇到的问题及解决办法
- 网络问题:在Try、Confirm或Cancel阶段可能因网络故障导致操作未成功执行。解决办法是引入重试机制,如通过定时任务对未完成的操作进行重试。同时结合状态机记录操作状态,确保不会重复执行已经成功的操作。
- 幂等性问题:某些操作可能不天然具备幂等性。可以通过数据库唯一索引来实现幂等,例如在订单创建时,以订单号作为唯一索引,多次尝试创建相同订单号的订单时,数据库会保证只有一次插入成功。还可以在服务端记录操作日志,每次操作前检查是否已执行过相同操作。
- 数据一致性问题:如果在Confirm或Cancel阶段部分服务成功,部分服务失败,会导致数据不一致。通过引入事务协调器(如使用阿里的Seata框架中的TC - Transaction Coordinator),它负责记录事务状态、协调各个服务的操作。若出现部分失败,协调器可发起补偿操作,确保所有服务最终数据一致。
基于Saga模式的设计方案
- 设计方案
- 事务分解:将一个大的分布式事务分解为多个本地事务。例如在一个电商的下单、支付、发货的流程中,分别将下单、支付、发货作为独立的本地事务。
- 事件驱动:每个本地事务完成后发布事件,下一个本地事务监听相关事件并触发执行。如订单创建成功后发布“订单创建成功”事件,支付服务监听该事件后进行支付操作,支付成功后再发布“支付成功”事件,发货服务监听此事件进行发货。
- 补偿事务:为每个本地事务设计对应的补偿事务。如果某个本地事务失败,按照相反顺序执行前面已成功本地事务的补偿事务。例如支付失败时,执行订单取消的补偿事务。
- 可能遇到的问题及解决办法
- 事件丢失:可能由于网络问题或消息队列故障导致事件丢失。引入可靠的消息队列(如RabbitMQ、Kafka等),它们提供消息持久化机制。同时采用消息确认机制,发送方等待接收方的确认消息,若未收到则进行重试。
- 补偿事务失败:补偿事务本身也可能失败。对补偿事务设置重试机制,并且记录补偿事务的执行状态。如果多次重试仍失败,需要人工介入处理,例如通过运维平台通知相关人员进行数据修复。
- 循环依赖:在事件驱动过程中可能出现服务之间的循环依赖,导致事务无法正常执行。通过梳理业务流程,避免出现循环依赖的设计。在设计阶段对服务间的依赖关系进行严格审查,使用有向无环图(DAG)来描述事务流程,确保依赖关系的合理性。