面试题答案
一键面试常见分布式事务解决方案分析
- XA协议
- 原理:XA 是一种基于两阶段提交(2PC)的协议。在第一阶段(准备阶段),所有参与事务的资源管理器(如数据库)会执行事务操作但不提交,向事务协调者报告是否准备成功。第二阶段(提交阶段),事务协调者根据所有资源管理器的准备结果决定是提交还是回滚事务。如果所有资源管理器都准备成功,则协调者通知所有资源管理器提交事务;否则,通知所有资源管理器回滚事务。
- 优点:XA 协议能严格保证事务的 ACID 特性,数据一致性强。
- 缺点:性能较低,因为在两阶段过程中,资源会被长时间锁定,可能导致死锁。而且 XA 协议依赖数据库对其的支持,对应用侵入性较大。
- TCC(Try - Confirm - Cancel)
- 原理:TCC 模式将事务分为三个阶段。Try 阶段主要是对业务资源进行初步检查和预留;Confirm 阶段在 Try 成功后真正执行提交操作;Cancel 阶段在 Try 失败或后续需要回滚时,对 Try 阶段预留的资源进行释放。
- 优点:不依赖数据库的事务支持,对业务代码侵入性相对较小,性能较高,因为它减少了资源锁定时间。
- 缺点:实现复杂,需要业务开发人员手动编写 Try、Confirm 和 Cancel 逻辑,并且要保证幂等性,否则可能导致数据不一致。
- Saga
- 原理:Saga 模式是将一个长事务分解为多个本地事务,每个本地事务都有对应的补偿事务。当某个本地事务失败时,Saga 会按照顺序调用之前已成功执行的本地事务的补偿事务来进行回滚,以保证数据的一致性。
- 优点:对业务侵入性小,适合长事务场景,性能较好,因为它不使用全局锁。
- 缺点:补偿逻辑编写复杂,并且 Saga 执行顺序可能会出现不一致问题,需要额外的机制来保证 Saga 的正确执行顺序。
在Spring Cloud环境中选用合适方案
- XA协议:适用于对数据一致性要求极高,业务操作执行时间短,并且数据库支持 XA 协议的场景。但由于其性能问题,在高并发微服务场景中较少使用。
- TCC:适合性能要求较高,业务逻辑相对简单,且能够较容易实现 Try、Confirm 和 Cancel 逻辑及幂等性的场景。例如一些对响应时间敏感的金融交易场景。
- Saga:适用于长事务场景,涉及多个微服务之间的复杂业务流程,且对一致性要求不是特别严格,允许一定时间内的数据最终一致性的场景。例如电商订单系统中包含多个复杂业务步骤的场景。
电商订单系统分布式事务设计思路
- 整体思路:选用 Saga 模式来设计该电商订单系统的分布式事务。因为电商订单系统涉及多个微服务,操作流程长,且允许一定时间内的数据最终一致性。
- 步骤
- 订单创建:订单微服务创建订单记录,状态设为“待支付”。
- 库存扣减:库存微服务接收到订单创建成功消息后,扣减相应商品库存。
- 支付:支付微服务接收到订单创建和库存扣减成功消息后,处理支付流程。
- 补偿逻辑:如果支付失败,调用库存微服务的补偿接口增加库存,同时订单微服务将订单状态设为“支付失败”。如果库存扣减失败,订单微服务取消订单记录。
关键代码片段
- 订单创建微服务
- 创建订单接口
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest orderRequest) {
Order order = orderService.createOrder(orderRequest);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
}
- 订单服务实现
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public Order createOrder(OrderRequest orderRequest) {
Order order = new Order();
order.setUserId(orderRequest.getUserId());
order.setStatus("待支付");
orderRepository.save(order);
return order;
}
}
- 库存扣减微服务
- 扣减库存接口
@RestController
@RequestMapping("/stock")
public class StockController {
@Autowired
private StockService stockService;
@PostMapping("/deduct")
public ResponseEntity<String> deductStock(@RequestBody StockDeductRequest request) {
boolean result = stockService.deductStock(request.getProductId(), request.getQuantity());
if (result) {
return new ResponseEntity<>("库存扣减成功", HttpStatus.OK);
} else {
return new ResponseEntity<>("库存不足", HttpStatus.BAD_REQUEST);
}
}
}
- 库存服务实现
@Service
public class StockService {
@Autowired
private StockRepository stockRepository;
public boolean deductStock(Long productId, int quantity) {
Stock stock = stockRepository.findByProductId(productId);
if (stock.getQuantity() >= quantity) {
stock.setQuantity(stock.getQuantity() - quantity);
stockRepository.save(stock);
return true;
}
return false;
}
}
- 支付微服务
- 支付接口
@RestController
@RequestMapping("/payment")
public class PaymentController {
@Autowired
private PaymentService paymentService;
@PostMapping("/process")
public ResponseEntity<String> processPayment(@RequestBody PaymentRequest paymentRequest) {
boolean result = paymentService.processPayment(paymentRequest);
if (result) {
return new ResponseEntity<>("支付成功", HttpStatus.OK);
} else {
return new ResponseEntity<>("支付失败", HttpStatus.BAD_REQUEST);
}
}
}
- 支付服务实现
@Service
public class PaymentService {
public boolean processPayment(PaymentRequest paymentRequest) {
// 模拟支付逻辑,例如调用第三方支付接口
return true;
}
}
- 补偿逻辑(以库存补偿为例)
- 库存补偿接口
@RestController
@RequestMapping("/stock")
public class StockController {
@Autowired
private StockService stockService;
@PostMapping("/compensate")
public ResponseEntity<String> compensateStock(@RequestBody StockCompensateRequest request) {
boolean result = stockService.compensateStock(request.getProductId(), request.getQuantity());
if (result) {
return new ResponseEntity<>("库存补偿成功", HttpStatus.OK);
} else {
return new ResponseEntity<>("库存补偿失败", HttpStatus.BAD_REQUEST);
}
}
}
- 库存补偿服务实现
@Service
public class StockService {
@Autowired
private StockRepository stockRepository;
public boolean compensateStock(Long productId, int quantity) {
Stock stock = stockRepository.findByProductId(productId);
stock.setQuantity(stock.getQuantity() + quantity);
stockRepository.save(stock);
return true;
}
}
以上代码仅为示例,实际应用中需要考虑消息队列用于异步通信、分布式事务协调等更多机制来确保数据一致性。