分布式事务
现在微服务越来越火了,我们的项目代码和数据库都被细分到各自的微服务里面,那么这种情况下,如何实现分布式的事务呢?
分布式事务的解决方案有很多,比如:2PC、3PC、TCC、Saga 和本地消息表等等。
2PC
2PC 也叫二阶段提交,是一种常用的分布式事务实现方法。我们用订单和优惠券的例子来说明一下,如何用 2PC 来解决订单系统和促销系统的数据一致性问题。在我们购物下单时,如果使用了优惠券,订单系统和优惠券系统都要更新自己的数据,才能完成“在订单中使用优惠券”这个操作。订单系统需要:在“订单优惠券表”中写入订单关联的优惠券数据;在“订单表”中写入订单数据。订单系统内两个操作的一致性问题可以直接使用数据库事务来解决。促销系统需要操作就比较简单,把刚刚使用的那张优惠券的状态更新成“已使用”就可以了。我们需要这两个系统的数据更新操作保持一致,要么都更新成功,要么都更新失败。
接下来我们来看 2PC 是怎么解决这个问题的。2PC 引入了一个事务协调者的角色,来协调订单系统和促销系统,协调者对客户端提供一个完整的“使用优惠券下单”的服务,在这个服务的内部,协调者再分别调用订单和促销的相应服务。
所谓的二阶段指的是准备阶段和提交阶段。在准备阶段,协调者分别给订单系统和促销系统发送“准备”命令,订单和促销系统收到准备命令之后,开始执行准备操作。准备阶段都需要做哪些事儿呢?你可以理解为,除了提交数据库事务以外的所有工作,都要在准备阶段完成。比如说订单系统在准备阶段需要完成:
- 在订单库开启一个数据库事务;
- 在“订单优惠券表”写入这条订单的优惠券记录;
- 在“订单表”中写入订单数据。
注意,到这里我们没有提交订单数据库的事务,最后给事务协调者返回“准备成功”。类似的,促销服务在准备阶段,需要在促销库开启一个数据库事务,更新优惠券状态,但是暂时不要提交这个数据库事务,给协调者返回“准备成功”。协调者在收到两个系统“准备成功”的响应之后,开始进入第二阶段。
等两个系统都准备好了之后,进入提交阶段。提交阶段就比较简单了,协调者再给这两个系统发送“提交”命令,每个系统提交自己的数据库事务,然后给协调者返回“提交成功”响应,协调者收到所有响应之后,给客户端返回成功响应,整个分布式事务就结束了。
2PC的解决方案:基于数据库XA协议、ZooKeeper watcher。
本地消息表
很多情况下,只需要保证最终一致性就可以了。比如说下单后清空购物车,可以在下单以后写一条清空购物车的日志,这个日志可以保存到数据库,也可以走消息队列,然后异步去执行这个操作,失败了可以进行重试。