侧边栏壁纸
博主头像
再见理想博主等级

只争朝夕,不负韶华

  • 累计撰写 112 篇文章
  • 累计创建 64 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

分布式事务相关理论及解决方案

再见理想
2022-05-27 / 0 评论 / 1 点赞 / 362 阅读 / 3,072 字

一,前言

什么是事务?

事务从狭义上来讲就是ACID的特性,原子性、一致性、隔离性、持久性,但是广义上的事务,尤其在分布式的环境下,我们更多强调它的一个原子性。

在没有网络隔离的本地事务,它是由内存总线和数据库的一个连接去保证他本地事务的一个原子性。

什么是分布式事务?

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。

简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

分布式事务解决方案有哪些?

分布式事务解决方案可分为 2PC 两阶段提交本地消息表TCC可靠消息最终一致性最大努力通知等几个类型。

分布式事务没有一个完美的解决方案,尽量还是在做服务拆分的时候,就把一些原子性的操作放在单进程但数据库里面去执行,由本地事务去保证他的 ACID。


二,CAP 理论

CAP 是 Consistency、Availability、Partition tolerance三个词语的缩写,分别表示一致性、可用性、分区容忍性

一致性
一致性是指写操作后的读操作可以读取到最新的数据状态,当数据分布在多个节点上,从任意结点读取到的数据都是最新的状态。

可用性
可用性是指任何事务操作都可以得到响应结果,且不会出现响应超时或响应错误。

分区容错性
通常分布式系统的各各结点部署在不同的子网,这就是网络分区,不可避免的会出现由于网络问题而导致结点之间通信失败,此时仍可对外提供服务,这叫分区容忍性

一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容忍性(Partition tolerance)这三项中的两项。它可以作为我们进行架构设计、技术选型的考量标准。

对于多数大型互联网应用的场景,结点众多、部署分散,而且现在的集群规模越来越大,所以节点故障、网络故障是常态,而且要保证服务可用性达到N个9(99.99…%),并要达到良好的响应性能来提高用户体验,因此一般都会做出如下选择:保证P和A,舍弃C强一致,保证最终一致性。


三,BASE 理论

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。

BASE 理论是对 CAP 中 AP 的一个扩展,通过牺牲强一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终达到一致状态。满足 BASE 理论的事务,我们称之为柔性事务

基本可用
分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如,电商网站交易付款出现问题了,商品依然可以正常浏览。

软状态
由于不要求强一致性,所以BASE允许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性,如订单的"支付中"、“数据同步中”等状态,待数据最终一致后状态改为“成功”状态。

最终一致
最终一致是指经过一段时间后,所有节点数据都将会达到一致。如订单的"支付中"状态,最终会变为“支付成功”或者"支付失败",使订单状态与实际交易结果达成一致,但需要一定时间的延迟、等待。


四,分布式事务对比分析

分布式环境下,一致性,可用性和 分区容错性只能满足其中两点,分布式事务模型根据 ACP 理论可以分为追求 AP 和追求 CP 类型。

追求 CP 强一致性的有XA、二阶段提交、三阶段提交。追求AP高可用的事务模型有 本地消息表、TCC、MQ通知等,他们属于最终一致性的解决方案。分布式事务整理如下图:

2PC 两阶段提交

最大的诟病是一个阻塞协议。RM在执行分支事务后需要等待TM的决定,此时服务会阻塞并锁定资源。由于其阻塞机制和最差时间复杂度高, 因此,这种设计不能适应随着事务涉及的服务数量增加而扩展的需要,很难用于并发较高以及子事务生命周期较长的分布式服务中。

对业务代码无侵入,通常都是在跨库的DB层面,最大的诟病是一个阻塞协议

分布式事务-两阶段提交(2PC、强一致性)
分布式事务- Seata实现2PC事务(强一致性)


XA

XA是一种基于2PC协议实现的规范。在2PC中没有明确资源是什么,以及资源是怎么提交的等等,而XA就是数据库实现2PC的规范,已知常用的支持XA的关系型数据库有Mysql、Oracle等。


本地消息表

本地消息表方案应该是业界内使用最为广泛的,因为它使用简单,成本比较低。本地消息表的方案最初是由eBay提出(完整方案),核心思路是将分布式事务拆分成本地事务进行处理。它的处理流程如下:

  1. 事务发起者把要处理的业务事务和写消息表这两个操作放在同一个本地事务里;
  2. 事务发起者有一个定时任务轮询消息表,把没处理的消息发送到消息中间件;
  3. 事务处理者从消息中间件获取消息,处理后通过MQ推送结果;
  4. 事务发起者更新消息表的状态;

从处理流程来看,本地消息表方案是一个基于消息中间件的可靠性来达到事务的最终一致性的方案。

原理图:
本地消息表根据业务的不同有不一样的设计,通用的字段有消息编号、消息内容、消息状态及创建时间等。

可能发生的情况及处理的策略:
需要注意消息接收方的幂等性处理。

总结:

将事务写入本地消息表,有个定时任务不断轮询这消息表。当然,重试次数达到一定上限后,可以告警通知哪个消息可能失败了,然后也可以做个页面展示本地消息表,并且可以针对每条消息有重试的按钮操作,因为有些可能是业务上有bug,等bug修复后,再手动重试下就能成功了。

我们经常说分布式事务最后一步就是人工兜底,到底怎么去兜底,就是这么处理的。


TCC事务

如果拿 TCC 事务的处理流程与 2PC 两阶段提交做比较,2PC 通常都是在跨库的 DB 层面,而 TCC 则在应用层面的处理,需要通过业务逻辑来实现。这种分布式事务的实现方式的优势在于,可以让应用自己定义数据操作的粒度,使得降低锁冲突、提高吞吐量成为可能。

而不足之处则在于对应用的侵入性非常强,业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作。此外,其实现难度也比较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。典型的使用场景:满,登录送优惠券等。

对应用的侵入性非常强,业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作。

分布式事务- TCC解决方案(补偿机制、最终一致性)
博客园:终于有人把 TCC分布式事务 说清楚了


可靠消息最终一致性

可靠消息最终一致性可通过 RocketMQ 实现。适合执行周期长且实时性要求不高的场景。引入消息机制后,同步的事务操作变为基于消息执行的异步操作, 避免了分布式事务中的同步阻塞操作的影响,并实现了两个服务的解耦。典型的使用场景:注册送积分,登录送优惠券等。

RocketMQ分布式事务(最终一致性)


最大努力通知

最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。采用MQ的 ack 机制就可以实现最大努力通知。

最大努力通知是分布式事务中要求最低的一种,适用于一些最终一致性时间敏感度低的业务;允许发起通知方处理业务失败,在接收通知方收到通知后积极进行失败处理,无论发起通知方如何处理结果都会不影响到接收通知方的后续处理;发起通知方需提供查询执行情况接口,用于接收通知方校对结果。典型的使用场景:银行通知、支付结果通知等。


总结

在条件允许的情况下,我们尽可能选择本地事务单数据源,因为它减少了网络交互带来的性能损耗,且避免了数据弱一致性带来的种种问题。若某系统频繁且不合理的使用分布式事务,应首先从整体设计角度观察服务的拆分是否合理,是否高内聚低耦合?是否粒度太小?分布式事务一直是业界难题,因为网络的不确定性,而且我们习惯于拿分布式事务与单机事务 ACID 做对比。

无论是数据库层的XA、还是应用层TCC、可靠消息、最大努力通知等方案,都没有完美解决分布式事务问题,它们不过是各自在性能、一致性、可用性等方面做取舍,寻求某些场景偏好下的权衡。

1

评论区