分布式事务相关概念及解决方案模型

一、基本知识概念

(一)数据库事务4大特性:ACID

数据库事务需要实现下面4大特性:

  1. A-Atomicity-原子性,指的是事务要么成功提交,要么失败回滚
  2. C-Consistency-一致性,指事务操作前后,数据库的完整性不会产生变化,因为事务不可能执行一半,导致数据库内容不正确
  3. I-Isolation-隔离性 事务不能互相影响,简单来说就是事务的中间状态不应该被其他事务察觉
  4. D-Durability-持久性 一旦事务提交则会永久保存在数据库中

其中,只有隔离性根据数据库不同的隔离级别有不同的标准,其他三点都是数据库事务必须具备的。

(二)分布式事务

单机事务机制可以有数据库本地事务实现良好的控制,但分布式事务由于涉及多个数据源。这种情况主要有两种:

  • 一种是分库分表导致了一台服务器多数据源,这种情况可以通过二阶段提交实现简单的事务控制。
  • 一种是应用的微服务化,一个请求由多个服务协作完成,而多个服务又配置了不同的数据源。这种情况如果是支付等严格要求数据一致性的场景可以用TCC事务模型控制,如果是要求并发性能,但对数据一致性要求不高可以用最终一致性方案控制。

在介绍事务控制模型之前先介绍一下针对分布式事务,我们需要关注的指标:

1.CAP理论

  1. C-Consistency -一致性,一个节点数据更新,其他节点保存的数据也应该同步更新
  2. A-Availability-可用性,节点收到请求,合理时间内必须返回基于请求合理的响应
  3. P-Partition tolerance-分区容错性 能够允许部分节点网络故障 CAP理论表示这三个指标必然不能同时满足,最多只能满足其中两个。这里简单推论一下:

同时满足这三个指标的情况相当于承认当一个节点无法与其他节点通信的时候,还能保证在合理时间返回与其他节点一致的合理响应。这显然是不可能的。由此可以得出3个指标不能同时满足。

因为P是分布式的基础,我们设计分布式系统不可能不考虑P,如果不考虑P那么这个分布式系统是极其不稳定的,所以针对CAP理论,一般选择满足CP或AP。

  • 选择CP意味着当出现部分节点通信挂了,为了一致性必须阻塞请求,等待数据同步。
  • 选择AP意味着当出现部分节点通信挂了,为了可用性,针对请求,节点只能返回一个不能保证一致性的返回给请求方。

顺带一提,基于此ZooKeeper集群是注重的是CP,当50%的节点挂了之后,那服务发现功能也就挂了。EureKa集群注重CA,注册中心只要有一台能够使用,则不会影响正常的服务发现,另外客户端还会缓存服务、消费者数据。

2.BASE理论

BASE理论算是对CAP理论的一种指导性意见。

  1. BA-Basically Available-基本可用,分布式系统出现不可预料故障的时候,要尽量将故障减小到不影响核心功能。
  2. S-Soft state-软状态,允许部分节点的数据存在一定的延时,这个延时不影响可用性,这是对强一致的一种妥协,因为真正的强一致是所有节点数据时时刻刻都保持一致,而CAP理论要求的C忽略了网络通信延迟。
  3. E-Eventually consistent -最终一致性,最终一致是指经过一段时间后,所有节点数据都将会达到一致。

总体上来说BASE理论强调的是用最终一致代替了CAP理论的强一致,用基本可用代替了CAP理论的完全可用。

二、分布式事务解决方案模型

(一)二阶段提交-2PC- XA Transactions

1.角色

在二阶段提交中涉及的角色有两个:

  • 事务管理器,指的是调度者,一般来说是单点服务器。
  • 本地资源管理器,一般指的是数据库服务,如Oracle,Mysql。

2.应用场景

单点应用,多数据源情况下。

3.原理

原理主要是利用了本地资源管理器自带的事务机制。

4.过程

  • 第一阶段,线程开启每个数据源的事务并执行业务逻辑,并阻塞等待每个数据源undo,redo日志写入完毕,就差提交
  • 第二阶段,如果每个数据源都没有发生异常,表示可以提交,则线程commit所有数据库的事务,如果有数据源异常了,则回滚所有事务。

强调一点,二阶段提交主要针对于,单点服务器多数据源,如果是多点服务,将引入事务管理器之间的分布式问题,所以二阶段提交模型只适用于单点服务,多数据源,强烈依赖数据库的事务支持。

5.缺点

缺点主要有:

  • 单点问题,如果事务管理器宕机,资源管理器将阻塞并且锁住资源
  • 同步阻塞,准备就绪到事务管理器发送请求这段时间对于资源管理器来说是不必要的阻塞与占用资源时间(资源管理器通过锁机制保证隔离性),对于性能上是一种浪费。
  • 没有完全解决数据不一致,不能保证事务管理器发出的commit命令操作正确到达本地资源管理器,如果通信失败了,事务管理器是无法察觉的,结果还是不一致的。

(二)TCC模型

1.角色

涉及的角色有微服务应用,不同数据源,同一个TCC管理框架,之所以要引用TCC管理框架的原因在于对于Try操作失败成功的感知,如果涉及微服务调用链是极其复杂的。

1.应用场景

微服务应用,不同数据源,不要求高并发,一致性要求较高。

2.过程

(1) 第一阶段-Try

根服务调用各个服务的Try方法,各个服务根据业务锁定资源-业务层面上的,没有引入数据库事务,就是将业务涉及的资源存入各个服务的本地方便之后回滚的时候恢复,然后返回给根服务是否执行成功。

(2) 第二阶段-Confirm或Cancel
  • Confirm,根服务获取各个服务try是否执行成功,若成功则调用各个服务的Confirm方法,confirm开始把锁定的资源正式刷到业务数据中,如果confirm不成功,则一直重试,务必保证confirm成功。
  • Cancel,如果各个服务的try有一个不成功,则调用各个服务的cancel方法,释放锁定的资源,如果调用不成功,也会重试。

注意:Try阶段成功意味着数据库层面的操作大概率是没有问题的,后续的异常只会发生在极端情况下,所以这是tcc根据try操作,去不停重试confirm或cancel的原因。

3.缺点

  • confirm和cancel方法因为有可能被重复调用,所以得从代码上保证幂等,对代码侵入性有点强,复杂度搞。
  • 由于整个TCC过程需要锁定资源所以对高并发场景不太友好。

(三)本地消息表方案模型

1.角色

  • 上游根服务
  • MQ消息中间件
  • 下游服务

2.思路

不关注MQ丢不丢消息,只关注根服务的本地事务,如果本地事务执行成功,那么记录所需发送的消息表也被插入成功。

此时唯一需要关注的只是消息能够被消费者消费掉,用轮询本地消息表的方式重复发送消息保证最终消息一致性即可。

3.具体方案

根服务完成事务后,将需要与其他服务协调的请求消息存入本地消息表并将状态置为待确认。然后有一个后台线程定时轮询消息表,将待确认消息推送给MQ,MQ再推送给消费者,消费者收到消息后,再消费消息。当消费完消息后,消费者通过一定方式如zookepper或接口回调根服务,然后根服务修改消息表状态为已完成。

3.缺点

强依赖于本地的消息表,数据库压力较大。

(四)最终一致性方案模型

1.角色

  • 上游根服务
  • 可靠消息服务
  • MQ消息中间件
  • 下游服务(可能有多个)

2.应用场景

主要应用于不要求强一致,并发要求度高,微服务异步调用场景中。

3.思路

这个模型主要关注三点:

  • 在根服务本地事务完成和失败的情况下,确保消息能够正确投递到消费者或取消投递。
  • 在下游服务本地事务失败的情况下,确保消息能够被重发。
  • 不关注各个优秀MQ框架消息可靠性的保证,即默认消息能够被丢失。

为了解决上述问题,引入了可靠消息服务,用以协调上游服务的事务状态和下游事务状态。通过维护消息的三种状态:待确认、已发送、已成功,确保事务的最终一致性。

3.过程

(1) 上游根服务调用可靠消息服务。

上游根服务同步调用可靠消息服务,参数包含调用下游服务的参数,这些数据由可靠消息服务存入自己的表中,并将这条记录的状态置为待确认。

(2) 上游根服务执行完自己的业务之后在再次调用可靠消息服务。

上游根服务执行完自己的业务之后,将业务执行结果传入可靠消息服务,可靠消息服务根据业务失败结果,若失败则删除消息表中关于这个业务的所有消息表记录,若成功则通过MQ发送消息给下游服务,然后更新刚刚存入的记录将状态置为已发送。注意可靠消息服务的这个方法中,判断业务成功失败,需安排在同一个本地事务(因为发送消息和更新记录必须保证原子性)。

(3) 下游服务接收消息之后消费消息,执行业务完成后回调可靠消息服务。

下游服务接收消息之后消费消息,执行业务完成后回调可靠消息服务。可靠消息服务受到请求后,将本地记录状态置为已完成。

4.注意

(1) 如何保证上游服务对可靠消息服务的100%可靠投递
  • 第一次调用如果失败,这种情况上游根服务会收到可靠消息服务错误返回,这种情况上游根服务直接放弃流程。
  • 第二次调用如果失败,这种情况可靠消息服务后台定时检查记录中为待确认消息的记录,如果待确认状态时间较长,则对上游服务发起请求,看上游服务是否完成了自己的业务逻辑。若完成,则自动更新记录状态,反之删除记录。(上游服务需要维护自己的事务执行状态在本地表中)
(2) 如何保证下游服务对可靠消息服务的100%可靠投递

可靠消息服务定时检查记录中已发送状态维持时间超时的记录,然后重复发送请求给下游服务。所以这里同时也要求下游服务方法的幂等性。

(4) 另外

这个模型和本地消息表服务很像,区别在于抽离了本地消息表,简化了根服务的操作,虽然消息表还是存在在可靠消息服务当中,但与业务库相独立,一定程度提高了性能。另外RocketMQ针对这种模型做了优秀的架构实现,相当于集成了上述的可靠消息服务的功能,并且没有消息表的存在,消息的状态由RocketMQ维护,由于我也没有实际使用过RocketMQ,所以就不展开介绍了。基于其他没有提供可靠消息确认机制,

(五)另外几种想到的解决办法

使用RabbitMQ的消息确认机制和消费者开启手动ACK是能够确保消息一定被消费。唯一需要关注的问题在于如何处理本地事务失败、消息者成功消费造成的数据不一致的情况。

对于这种情况,可以这么做:

  • 在发送消息的时候,绑定两个监听队列到一个交换机上。一个监听队列负责对这种情况进行补偿,补偿操作为判断本地事务是否成功,若不成功则进行补偿。一个监听队列负责完成下游服务。

三、小结

其实这些模型有很多都有相似的地方,也能做很多变通,很多通过回调、轮询、长连接监听、同步调用的方式其实都可以根据具体场景互相替换或选择不同的优秀架构。另外需要注意的就是,引入分布式事务是需要维护成本和性能成本的,很多情况下,需要根据业务来看需不需要引入分布式事务。不是很严格的情况,用监控、日志记录的方式采取人工补偿的策略有可能成本更小。

转载于:https://my.oschina.net/u/4101481/blog/3079845

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值