分布式事务方案简介

分布式事务简介

分布式事务存在的场景:

      当下互联网发展如火如荼,绝大部分公司都进行了数据库拆分和服务化(SOA)。在这种情况下,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,当需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据的操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。

1、跨库事务

      跨库事务指的是,一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据。下图演示了一个服务同时操作2个库的情况:

2、分库分表

 

 

      通常一个库数据量比较大或者预期未来的数据量比较大,都会进行水平拆分,也就是分库分表。如下图,将数据库B拆分成了2个库:

3、服务化

 

 

      微服务架构是目前一个比较一个比较火的概念,服务化拆分后,独立服务之间通过RPC框架来进行远程调用,实现彼此的通信。下图演示了一个3个服务之间彼此调用的架构:


 

主流解决方案:

1、X/Open DTP模型与XA规范:

      在DTP本地模型实例中,由AP、RMs和TM组成,不需要其他元素。AP、RM和TM之间,彼此都需要进行交互,如下图所示:

      XA规范的最主要的作用是,定义了RM-TM的交互接口,下图更加清晰了演示了XA规范在DTP模型中发挥作用的位置,从下图中可以看出来,XA仅仅出现在RM和TM的连线上。

   

两阶段提交协议(2PC):

      两阶段协议(two-phase commit)是在OSI TP标准中提出的;在DTP参考模型中,指定了全局事务的提交要使用two-phase commit协议;而XA规范只是定义了两阶段提交协议中需要使用到的接口,也就是上述提到的RM-TM交互的接口:

image.pngimage.png

 

2pc协议出现的问题:

1、同步阻塞问题一个全局事务内部包含了多个独立的事务分支,这一组事务分支要不都成功,要不都失败。各个事务 分支的ACID特性共同构成了全局事务的ACID特性。也就是将单个事务分支的支持的ACID特性提升一个层次(up a level)到分布式事务的范畴。如果追求强一致性的话最好将事务的隔离级别设置为serizable,而serizable是处理性能最差的事务隔离级别。

2、单点故障: 由于协调者的重要性,一旦协调者TM发生故障。参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。

3、数据不一致:在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。

 

改进措施:

     为了解决2节点提交协议的问题,后面又提出了三阶段提交协议3PC与两阶段提交不同的是,三阶段提交有两个改动点:

    1、引入超时机制。同时在协调者和参与者中都引入超时机制。

    2、在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommitPreCommitDoCommit三个阶段。

     相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。

 

结论:

    1.无论是二阶段提交还是三阶段提交都无法彻底解决分布式的一致性问题。

    2.二阶段和三阶段都是在资源层面解决分布式事务问题,但是追求强一致性,资源加锁时间长,对性能有影响。

 

2、柔性事务方案

2.1、最大努力通知:

     最大努力通知型是最简单的一种柔性事务,适用于一些最终一致性时间敏感度低的业务,且被动方处理结果不影响主动方的处理结果。典型的使用场景:如银行通知、商户通知等。最大努力通知型的实现方案,一般符合以下特点:

   1、不可靠消息:业务活动主动方,在完成业务处理之后,向业务活动的被动方发送消息,直到通知N次后不再通知,允许消息丢失(不可靠消息)。

   2、定期校对:业务活动的被动方,根据定时策略,向业务活动主动方查询(主动方提供查询接口),恢复丢失的业务消息。

 

典型场景:

   

 

 

 

短信发送流程如下:

1、业务方将短信发送请求提交给短信平台

2、短信平台接收到要发送的短信,记录到数据库中,并标记其状态为”已接收"

3、短信平台调用外部短信发送供应商的接口,发送短信。外部供应商的接口也是异步将短信发送到用户手机上,因此这个接口调用后,立即返回,进入第4步。

4、更新短信发送状态为"已发送"

5、短信发送供应商异步通知短信平台短信发送结果。而通知可能失败,因此最多只会通知N次。

6、短信平台接收到短信发送结果后,更新短信发送状态,可能是成功,也可能失败(如手机欠费)。到底是成功还是失败并不重要,重要的是我们知道了这调短信发送的最终结果

7、如果最多只通知N次,如果都失败了的话,那么短信平台将不知道短信到底有没有成功发送。因此短信发送供应商需要提供一个查询接口,以方便短信平台主动的去查询,进行定期校对。

 

2.2、TCC两阶段补偿型:

    TCC的作用主要是解决跨服务调用场景下的分布式事务问题。TCC是Try-Confirm-Cancel的简称:

如下图所示分为三个阶段:

Try阶段:

   完成所有业务检查(一致性),预留业务资源(准隔离性)

Confirm阶段:

   确认执行业务操作,不做任何业务检查, 只使用Try阶段预留的业务资源。

Cancel阶段:

   取消Try阶段预留的业务资源。

 

 

 

TCC方案和XA方案的区别:

1. XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。

    TCC中的两阶段提交并没有对开发者完全屏蔽,也就是说从代码层面,开发者是可以感受到两阶段提交的存在。try、confirm/cancel在执行过程中,一般都会开启各自的本地事务,来保证方法内部业务逻辑的ACID特性。其中:

   1、try过程的本地事务,是保证资源预留的业务逻辑的正确性。

   2、confirm/cancel执行的本地事务逻辑确认/取消预留资源,以保证最终一致性,也就是所谓的补偿型事务

由于是多个独立的本地事务,因此不会对资源一直加锁。

 

结论:  

 优点:XA两阶段提交资源层面的,而TCC实际上把资源层面二阶段提交上提到了业务层面来实现。有效了的避免了XA两阶段提交占用资源锁时间过长导致的性能地下问题。

 缺点:主业务服务和从业务服务都需要进行改造,从业务方改造成本更高。

 

2.3、最终一致性方案:

       是指产生消息的业务动作与消息发送的一致。也就是说,如果业务操作成功,那么由这个业务操作所产生的消息一定要成功投递出去(一般是发送到kafkarocketmqrabbitmq等消息中间件中),否则就丢消息。

2.3.1、事务消息:

       基于RocketMQ的事务消息进行处理:

       

 

 

事务消息的逻辑,由发送端 Producer进行保证(消费端无需考虑)

   1、发送一个事务消息,这个时候,RocketMQ将消息状态标记为Prepared,注意此时这条消息消费者是无法消费到的。

  2、执行业务代码逻辑,可能是一个本地数据库事务操作。

  3、确认发送消息,这个时候,RocketMQ将消息状态标记为可消费,这个时候消费者,才能真正的保证消费到这条数据。

      注:确认消息发送失败场景:RocketMQ会定期扫描消息集群中的事务消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认。RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

      如果消费失败怎么办?阿里提供给我们的解决方法是:人工补偿。

   

2.3.2、独立消息服务/本地消息表:

     并不是所有的mq都支持事务消息。也就是消息一旦发送到消息队列中,消费者立马就可以消费到。此时可以使用独立消息服务、或者本地消息表。

  

 

 


 

      将消息先发送到一个我们自己编写的一个"独立消息服务"应用中,刚开始处于prepare状态,业务逻辑处理成功后,确认发送消息,这个时候"独立消息服务"才会真正的把消息发送给消息队列。消费者消费成功后,ack时,除了对消息队列进行ack,对于独立消息服务也要进行ack,"独立消息服务"一般是把这条消息删除。而定时扫描prepare状态的消息,向消息发送端(生产者)确认的工作也由独立消息服务来完成。

      对于"本地事务表",其实和"独立消息服务"的作用类似,只不过"独立消息服务"是需要独立部署的,而"本地事务表"是将"独立消息服务"的功能内嵌到应用中。

2.3.3、Saga方案:

      长事务解决方案,异步执行,最终一致性事务方案。

      

 

 

      分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

       Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

posted @ 2020-06-02 20:29  风好大  阅读(261)  评论(0编辑  收藏  举报