关于分布式事务的思考

分布式事务产生的原因

  • 数据库分库分表,无法保证夸库事务一致(单库数据库自身就可以保证)
  • 微服务化
  • 微服务架构中,每个服务在调用本地事务的时候,知道自己执行的事务是成功还是失败,但是无法知道其他服务节点的事务执行情况,因此需要引入协调者TM,负责协调参与者RM的行为,并最终决定这些参与者是否把事务进行提交。

随着微服务的流行,让分布式事务的问题日益突出,那么常见的分布式事务解决方案有哪些?如何理解最终一致性和他的事务补偿机制呢?

刚性事务 - 强一致性

img

这个是标准的全局事务,事务管理器控制着全局事务,管理事务的生命周期,并通过XA协议与资源管理器协调资源,资源管理器负责控制和管理实际的资源(这里的资源管理器,可以是一个DBMS,或者消息服务管理系统)

两阶段提交

它是XA用于在全局事务中,协调多个资源的机制,常用于事务管理器和资源管理器之间,解决一致性问题,分两个阶段:

  • 提交事务请求
  • 执行事务请求
img

2PC的问题

效率低,与本地事务相比,XA协议的系统开销比较大(数据被锁定的时间跨度整个事务,直到全局事务的结束),只有支持XA协议的资源才能参与分布式事务。

2PC是反可伸缩模式的,在事务处理过程中,参与者需要一直持有资源直到整个事务的结束,这样当业务规模越来越大的情况下,它的局限性就越明显。

数据不一致,在2pc中的第二阶段时,当TM向RM发送提交请求之后,发生局部的网络异常或者在发送提交请求过程中TM发生故障, 这会导致只有一部分RM收到了提交请求,然后没有收到提交请求的RM不会执行事务的提交,于是整个分布式系统便会出现数据不一致。

单点故障, 由于TM的重要性,一旦发生故障,整个事务失效

3PC的改进

增加了超时机制, 主要解决单点故障问题,并减少资源锁定时间,一旦RM无法及时收到来至TM的信息之后,它会默认执行Commit操作, 而不会一直持有事务资源并处于阻塞状态。但是这种机制同样会导致数据不一致的问题,由于网络的原因,TM发送的回滚动作,没有被RM及时的收到,那么RM等待超时后就执行了提交操作,这样就和收到回滚操作并执行的RM之间存在了数据不一致的情况。

柔性事务-最终一致性

在2008年,eBay公布了基于BASE准则的最终一致性解决方案,它主要采用了消息队列来辅助实现事务控制流程,其核心通过消息队列的方式来异步执行分布式处理的任务,如果事务失败,则可以发起人工重试的纠正流程(比如对账系统,对处于dead letter queue的问题进行处理)

消息发送一致性

微服务架构下,需要通过网络进行通信,就自然引入了数据传输的不确定性,也就是CAP原理中的P-分区容错,而这里的消息发送一致性是可靠消息的保证。

生成消息的业务动作与消息发送的一致(e.g: 如果业务操作成功,那么由这个业务操作所产生的消息一定会成功投递出去,否则就丢失消息)

img

如上图,保证消息一致性的一般流程如下:

  • Producer 先把消息发送给消息中间件服务,消息的状态标记为待确认,这个状态不会被Consumer消费,对于长期待确认的消息,消息中间件会调用Producer的查询接口,查看最新状态,根据结果决定是否删除消息。
  • Producer 执行完业务操作后,向消息中间件发送确认消息,这时消息的状态更改为待发送(可发送)
  • Consumer 监听并接收到待发送状态的消息,执行业务处理
  • Consumer 业务处理后向消息中间件服务发送ACK,确认消息已经收到(消息中间件删除 该消息)

消息的ACK确认流程中,任何一个环节都可能会出问题!

未ACK的消息,采用按规则重新投递的方式进行处理(很多MQ都提供at least once的投递,持久化和重试机制),一般还会设置重发的次数, 超过次数的消息会进入dead letter queue,等待人工干预或者延后定时处理。

业务接口的幂等性

消息的重复发送会导致业务接口出现重复调用的问题,主要原因就是消息没有及时收到ACK确认导致的, 那如何实现幂等性设计呢?

在实际的业务场景中, 业务接口的幂等性设计,常结合查询操作一起使用,

比如根据唯一标识查询消息是否被处理过, 或者根据消费日志表,来维护消息消费的记录。

保证最终一致性的模式

  • 可查询模式,任何一个服务操作都提供一个可查询接口,用来向外部输出操作执行的状态,下游Consumer可以通过接口得知服务操作执行的状态,然后根据不同的状态做不同的处理操作(执行或者取消), 该模式对业务接口有一定侵入性。
  • 补偿模式, 有了查询模式,我们能够知道操作的具体状态,如果处于不正常状态,我们可以修正操作中出现的问题,或许是重新执行,或许取消已经完成的操作,通过修复是的整个分布式系统达到最终一致。
  • 最大努力通知模式, 在调用支付宝交易接口或微信支付接口时,一般会在回调页面和接口里,解密参数,然后调用系统中更新交易状态相关的服务,将订单更新为付款成功。同时,只有当回调页面中输出了success字样或者标识业务处理成功相应状态码时,支付宝才会停止回调请求。否则,支付宝会每间隔一段时间后,再向客户方发起回调请求,直到输出成功标识为止。

思考如下:

对于业务方与消息队列的交互,可以直接入本地数据库,通过本地事务保证

1、业务处理成功

2、消息入数据库

因为二者在同一个事务中

接下来就可以起一个任务 轮询消息表,发往消息中心,只要发送失败,继续发送,收到正确的入队返回为止!

最后消息中心可以不停的push(或者consumer pull) 来处理消息。

这种方式 也要求下游业务做好幂等,消息中心只要收不到 正确的 ack ,就会存在消息重复消费的问题!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容