分布式事务的实现
分布式事务
概念
-
狭义
有两个或者两个以上的网络数据库主机同时参与的事务
-
广义
在分布式、微服务架构流行以后,分布式事务多指的是一个业务场景,需要编排很多独立部署的服务时,如何保证业务整体的原子性与一致性问题
在微服务架构中,随着服务的逐步拆分,数据库私有已经成为共识,这也导致所面临的分布式事务问题成为微服务落地过程中一个非常难以逾越的障碍,但是目前尚没有一个完整通用的解决方案。
其实不仅仅是在微服务架构中,随着用户访问量的逐渐上涨,数据库甚至是服务的分片、分区、水平拆分、垂直拆分已经逐渐成为较为常用的提升瓶颈的解决方案,因此越来越多的原子操作变成了跨库甚至是跨服务的事务操作。最终结果是在对高性能、高扩展性,高可用性的追求的道路上,我们开始逐渐放松对一致性的追求,但是在很多场景下,尤其是账务,电商等业务中,不可避免的存在着一致性问题,使得我们不得不去探寻一种机制,用以在分布式环境中保证事务的一致性
产生的背景及原因
-
在单体数据库应用中,数据库主要通过锁和保存在本机硬盘上的日志(例如Oracle中有redo日志和undo日志)信息来保证事务的ACID特性
-
在分布式环境下,由于物理分区和网络分区的存在会导致以下一些问题:
-
日志信息保存在各自的硬盘上,没有关联性
-
事务参与者无法相互感知其他事务参与者
-
各个服务需要通过网络交互,但网络是不稳定,而且这种不稳定是无法解决的,所有服务之间无法很好的保持同步性和一致性
-
多种分布式事务一致性
-
强一致性
-
2PC(two-phase commit protocol)
-
优势
-
实现简单
-
数据一致性较高(无法完全保证)
在二阶段提交的阶段二中,当协调者向参与者发送请求之后,发生了局部网络异常或者在发commit请求过程中协调器发生了故障, 这会导致之后一部分参与者接收到了commit请求, 而在这部分参与者接到commit请求之后会执行commit操作, 但是其他部分未接到commit请求的机器则无法执行事务提交, 于是整个分布式系统便出现了数据不一致的现象
-
对业务侵入小
-
-
劣势
-
目前(2021-03-25)只有DTC(TransactionScope)支持2PC协议,但是DTC只有.Net Framework支持,.Net core不支持
-
由于需要锁定资源,性能较差;并且不同的本地资源管理器对资源的锁定行为不一致,从而会导致不确定性;举个例子:在SqlServer中,在两阶段提交事务中被修改的数据不能被别的事务查询和修改;但是在Oracle中,在两阶段事务中被修改的数据还可以进行读取操作而不会发生阻塞
-
事务管理器单点故障问题
-
消息丢失问题(丢失提交或者是回滚消息)
-
数据库支持完善度(MySQL 5.7之前都有缺陷)
-
并不是所有资源都支持2PC协议,比如MongoDB
-
-
实现方式
TransactionScope;需要DTC(Distributed Transaction Coordinator)服务支持
-
3PC(three-phase commit protocol)
-
-
优势
-
实现简单
-
进一步提高了数据一致性(无法完全保证)
-
由于有PreCommit这步操作会把Redo日志和Undo日志都固化到硬盘,当任何一个本地资源管理器在一定时间范围内没有接收到事务协调器的提交命令时会自动提交PreCommit步骤中做的事情
-
当PreCommit发生错误回滚的过程中,如果网络中断,本地资源管理器接收不到回滚命令时会导致无法回滚;如果只有部分本地资源管理器收到回滚命令时会导致数据不一致
-
-
对业务侵入小
-
-
劣势
-
目前(2021-03-25)没有支持
-
由于需要锁定资源,性能较差;并且不同的本地资源管理器对资源的锁定行为不一致,从而会导致不确定性;举个例子:在SqlServer中,在两阶段提交事务中被修改的数据不能被别的事务查询和修改;但是在Oracle中,在两阶段事务中被修改的数据还可以进行读取操作而不会发生阻塞
-
事务管理器单点故障问题
-
消息丢失问题(丢失回滚消息)
-
数据库支持完善度(MySQL 5.7之前都有缺陷)
-
并不是所有资源都支持3PC协议
-
-
-
弱一致性
允许数据在一定时间内不一致,但是很快就可以变成一致
-
实现机制
TCC(Try---Confirm---Cancel)
-
-
优势
-
由于通过Try步骤预留了资源,不需要长时间锁定资源,所以并发比较高
-
数据一致性较好(无法完全保障)
如果调用Cancel时一直失败时会导致数据不一致
-
-
劣势
-
对业务侵入大
-
开发成本高,原本只需要做一件事情的现在需要做三件事情(try,confirm,cancel)
-
开发难度大
-
业务需要分段设计
-
业务需要允许空回滚(重复调用Cancel操作)
-
业务需要悬挂控制(try阻塞,先调用cancel的情况下需要保证数据的正确性)
-
可见控制(临时状态和值在什么地方哪里可以展示和不可以展示)
-
并发访问控制,因为无需锁定资源,并发会很高,需要保证高并发情况下数据的正确性
-
幂等性控制(TCC多少次,结果不变)
-
最终一致性(BASE理论)
-
理论概念解释
在分布式和微服务环境下,可用性是最重要的,但是根据CAP理论,三者不可兼得,在我们选择了A的时候就牺牲了C,于是为了更好地保证数据一致性,EBay公司提出了BASE理论。
-
理论内容
-
Basically Available(基本可用)
-
Soft state(软状态)
-
Eventually consistent
-
-
实现机制
可靠消息最终一致性
-
消息中间件
-
本地事务表
-
-
-
-
-
如何保证消息的可靠性
消息投递的可靠性
-
事务消息的保存和业务逻辑在同一个本地事务中,可以保证事务的ACID
-
在本地事务提交成功以后把事务消息发送到消息中间件
-
如果事务提交以后由于各种原因事务消息没有发送成功的话,定时轮询把发送失败的事务消息再次发送
NOTICE 1: 消息发送不能无限次的重试,当达到设置的极限重试次数以后需要人工介入
NOTICE 2: 消息重试发送的过程中有可能导致消息多次发送,业务需要保证幂等性
消息中间件持久化的支持
-
RabbitMQ中,对消息持久化的各个环节中都进行了必要的设置,当消息被RabbitMQ接收以后,RabbitMQ可以保证消息的持久化
消息接收的可靠性
-
对每个到达的消费者的消息,进行检测,如果是事务消息,先尝试保存到数据中,如果保存失败,那么会把事务消息重新入队,再次尝试保存到数据库中,直到保存成功。
-
数据库保存成功以后,把事务消息分发到对应的Handle方法进行业务处理,处理完成以后(不管成功,失败)对事务消息进行ACK,如果业务处理失败,把事务消息标记成异常,等待下一轮重试,直到重试次数达到极限设置值
NOTICE 1: 消息处理不能无限次的重试,当达到设置的极限重试次数以后需要人工介入
NOTICE 2: 由于消息很有可能会进行重试,业务需要保证幂等性
NOTICE 3: 在事务消息处理完成以后对状态的回写和对事务消息进行ACK这个操作组没有事务保证,所以有可能状态回写成功,但是没有ACK成功。这会导致消息重试,需要业务保证幂等性
幂等性控制
概念
对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源 的影响是一致的
幂等性控制方式
-
MVCC多版本并发控制—乐观锁---数据库更新时带上版本号—更新 +1 条件必带version-----id + version
-
去重表---请求带个guid---操作前校验下guid不能重复—文章id+用户id+唯一索引
-
Token机制---每次操作都带个唯一id,请求来了先检测再执行
-