分布式事务的几种方案

最近正在给公司做分布式事务解决方案。也看了不少资料,对分布式事务的几个方案也有了一些自己的理解。这里写个博客记录一下学习过程,再说说结合业务相关内容。

CAP原则

CAP原则指出三个原则:一致性,可用性,还有分区容错性。

一致性:指分布式系统中的各节点的数据一致性。A中存储V=10,B中也要是V=10。才满足一致性原则。

可用性:指分布式系统中每次访问,都有可用节点可以对请求进行响应。

分区容错性:分布式架构部署在多个节点,即分区。通过请求响应进行通信,所以分布式要允许节点间出现错误,比如网络闪断等。

 

标准分布式事务,基于XA协议的两阶段提交2PC

这种是使用基于XA协议的分布式事务,主要分事务协调器和资源管理器两部分。资源管理器一般由DBMS实现。首先业务从事务协调器请求到各个子事务中。这时候子事务并不提交,返回事务协调器OK,然后事务协调器等待接受全部子事务返回,如果全部返回成功,则事务协调器让所有子事务进行提交。这样保证了分布式事务的强一致性。

但是这种方案也有缺点,目前XA协议支持的比较好的oracle和DB2,都是需要付费的。而mysql支持较差。并且由于事务协调器的存在,在高并发下,一个业务要发送接受多次请求。并且在长事务期间一直持有资源。

这里借用他人图展示一下:

 

 

消息事务,确保最终一致性

我们刚说了2PC保证强一致性,即任何时候访问数据所有节点数据一致。与之相对的是最终一致性,即在业务处理中,有可能短时间出翔数据不一致的情况。但最终的结果数据都是一致的。下面说一下这种方案,就是利用本地业务事务处理+发送消息到消息到消息服务系统,利用状态机控制、发送消息,确保消息的可靠性投递。达到最终一致性。

大概一个流程:

1,A系统在事务开始时,将消息发送到我们的消息服务系统,消息状态为待确认:

    如果A发送异常,几种情况:

        A),消息服务系统没收到消息,A回滚,对系统业务无影响。

        B),消息服务系统收到消息出现异常,对系统业务无影响。

        B),消息服务系统收到了消息,A接收到回传的如timed out信息。A回滚,B中一直存在一条待确认消息。后续通过消息状态回查机制处理。

2,消息服务系统返回A系统消息待确认保存成功。

3,A系统继续进行业务处理。如果业务处理出现异常:

    A回滚。并像消息服务系统发送一个消息取消的请求,取消请求同样面临几个问题:

        A),消息服务没收到。

        B),消息服务收到但处理异常了。

        C),消息服务收到处理成功,但A系统没收到返回。

     消息服务系统中的这条消息将一直处于待确认状态。后续通过消息状态回查机制处理。

4,A系统中业务处理完成后,事务提交,A中业务处理完毕,再次向消息服务系统发送请求,告诉其该消息可以确认并发送。消息服务系统接到确认并发送请求,同步返回确认结果。

    这时候如果异常,可以分两种情况:

        A),消息服务系统没收到消息。

        B),消息服务系统收到消息确认并发送但处理出现异常。

        C),消息服务处理成功,回传A系统出现如timed out等问题。

        这三种情况,那么消息将一直处于待确认状态,但是A系统中的业务已经处理完毕并且A中事务已经提交,消息后续通过消息状态回查机制进行处理。

6,消息服务系统使用异步或MQ发送消息。

    这里分两种情况

    一个是发送http,一个是发送mq。

    先说发送http,分以下几种情况:

        A),B系统没接收到请求,消息服务将此类型消息进行记录,使用补偿重发机制进行重发。

        B),B系统接收到请求并抛出也无异常,消息服务系统将此类型消息进行记录,并 人工进行处理。

        C),B系统接收到请求并返回成功,但消息服务系统因为timed out等原因没收到返回内容,消息服务系统将通过重发机制进行重发,这里需要注意一个问题,就是被动接受方暴露的接口,一定要实现幂等!幂等!幂等!

    再说一下发送MQ,这里我就用我熟悉的rabbitMQ来叙述了。

    消息发送到MQ,MQ接受成功并将消息持久化,然后返回消息服务系统callback。

    如果发送MQ成功,将消息状态变更为发送成功,待处理。

    如果发送MQ失败,没收到callback,消息将通过重发机制进行重发到MQ。这时如果超出了最大重试次数,那么变更状态为死信,进行人工处理。

    这时消息到达MQ,消费端监听队列进行消息消费。

    如果消费端处理成功后,通过ACK机制将消息ACK。并反向发送一个MQ消息到消息服务的一个监听服务。告诉消息服务消息成功被消费。

    如果消息消费出现异常,那么MQ反向发送异常信息到消息服务系统。消息服务系统更新消息状态并更新异常信息。后续进行人工处理。

 

然后补充一下上述的几点内容:

消息状态回查机制:这个是发送系统要提供给消息服务的接口,当出现异常时,去查消息在系统中的状态。用来确认丢弃消息还是确认并发送消息。

补偿重发机制:这个是当消息服务以http形式发送时,出现了非业务异常,如timed out等。使用补偿重发机制定时抓取此类消息进行重推。

幂等:这个方案的关键所在是消息消费端暴露接口一定要实现幂等。不然有可能会出现同一条消息被多次消费的问题。

监听消费端返回内容:当使用MQ发送时,需要消息服务监听一个消息队列来更改通过MQ发送的消息的返回内容。更新消息服务内部消息状态。

 

这里用本地事务+消息可靠性投递实现的分布式事务的可靠性和最终一致性。使用上比较看重业务需要,是否能接受数据的最终一致性原则。其实上面这个流程稍显复杂了。有几个可以改造的地方:

1,将消息服务系统改造成本地消息服务,发送方A系统直接引入消息存储模块,消息队列发送模块,将待确认,确认并发送,取消接口合并为一步保存并发送,待A业务处理完成后和消息落库放入同一个事务。确保消息存储和业务处理同时成功或失败。然后通过发送模块对消息进行发送。

这样也有一个问题就是业务处理和发送消息没有成功解耦,业务处理就要和发送消息必须放在一个事务当中了,当发送失败时,业务处理也要跟着回滚。不然业务处理成功却没发送成功的情况就会出现。业务还持久化到DB中了。就会出现严重的系统问题。

2,还有一些比如可以使用redis进行队列发送。使用一些nosql数据库替代mysql进行消息存储等。

这是目前我能想到的内容。经过这一系列的处理,可以将分布式事务拆分为本地事务+消息可靠性投递。虽然损失了一部分的数据的强一致性。但用最终一致性进行弥补。并且不会因为长事务带来过多的性能损耗。使用这种方式主要是看业务是否需要强一致性或者是否能接受这种最终一致性。并且我觉得更适合于一些和三方系统对接时,各处理各的。补偿系统更像一个最大努力通知型处理方案。并且有一个致命缺点就是无法避免人工处理这个问题。

3,TCC

TCC是一个类似于两阶段提交的一个方案。使用try预留资源,confirm进行提交,当出现问题使用cancel进行业务回滚操作。

拿业务举个例子,比如买10本书,需要扣减库存,增加积分,先将订单状态置为waiting_confirm,然后发送库存系统,库存找到书这个SKU,库存100本,冻结10本;积分系统增加10待增加积分。 这就是try阶段,预留资源。当预留资源阶段成功后,执行confirm阶段,这阶段一般不出现问题。将预留的资源加到订单中,库存冻结10本变成0证明卖出10本;积分系统将待增加的10积分增加到积分余额中。订单状态变成已发货。业务完成。但是当try阶段出现异常时,会直接执行cancel阶段,取消之前预留的资源,将库存中冻结的10本恢复到库存中;将积分中待增加的积分去除本次待增加积分。订单变成已取消。

TCC模式和业务耦合性更高。一般都是每个业务不同都要写一套TCC,将之前的一个接口变成3个接口。有点类似于2阶段提交,但是2阶段提交是基于XA协议的事务提交,这个是基于业务的。

 

这3种是目前我了解到的一些关于分布式事务的内容,我们正在做的是打算第2种和第三种结合使用。在强一致性要求时,使用TCC形式的分布式事务方案。保证一致性的同时尽量保证可用性。但和三方系统的一些对接过程中,计划使用基于消息服务系统的确保消息的最终一致性方案。因为各个系统都有自己的处理。不太可能要求三方系统和你一起进行回滚操作。比如第三方仓库发货软件,他发货就是发货了。其他地方出了异常,他也不会回滚,回滚也没有意义,因为货物确实发出了。所以我们只能确保消息的可靠性投递并且保证数据的最终一致性。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值