分布式事务

分布式事务类型:

分布式事务处理机制共有四种:

  1. 两阶段提交
  2. TCC事务(事务补偿)
  3. 本地消息表(异步确保),
  4. MQ事务消息。

两阶段提交:

  与数据库XA事务一样,两阶段提交使用XA协议。
  两阶段提交这种方案属于牺牲了一部分可用性来换取的一致性。

优点:

  尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。

缺点:

  实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景,如分布式系统跨接口调用。

TCC事务:

  TCC其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿操作。分为三个阶段:

  1. Try阶段主要是对业务系统做检测和资源预留。
  2. Confirm阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行Confirm阶段时,默认Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
  3. Cancel阶段主要是在业务执行错误,需要回滚的状态下,执行的业务取消,预留资源释放。

优点:

  跟2阶段提交比起来,实现及流程相对简单了些,但数据的一致性也要比2阶段提交要差一些。

缺点:

  在2,3步中都可能失败。TCC是一种应用层的补偿方式,需要程序员在实现时写很多补偿的代码,一些场景中,一些业务流程用TCC不太好定义及处理。

本地消息表:

  使用最多的,核心思想是将分布式事务拆分成本地事务进行处理,来源于ebay。

基本思路就是:

  消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后会经由MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。
  消息消费方,需要处理这个消息,并完成这个的业务逻辑。如果此时本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。
  生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息在发送一遍。
  这种方案遵循最终一致性。

优点:

  一种非常经典的实现, 避免的分布式事务,实现了最终一致性。

缺点:

  消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

MQ事务消息:

  有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但一些主流的MQ都是不支持事务消息的,如RabbitMQ和Kafka都不支持。

以阿里的RocketMQ为例:

  第一阶段Prepared消息,会拿到消息的地址。
  第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
  也就是说在业务方法内部要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了,RocketMQ会定期扫描消息集群中的事务消息,这是发现了Prepared消息,它会像消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

优点:

  实现了最终一致性,不需要依赖本地数据库事务。

缺点:

  实现难度大,主流MQ不支持,RocketMQ事务消息部分代码未开源。