分布式事务

本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

1.  分布式理论

1.1.  CAP定律

CAP指的是:一致性(Consistency)可用性(Availability)分区容错性(Partition tolerance)

CAP定律说的是,在一个分布式系统中,最多只能满足C、A、P中的两个,不可能三个同时满足。

在分布式系统中,网络无法 100% 可靠,分区其实是一个必然现象。如果我们选择了 CA 而放弃了 P,那么当发生分区现象时,为了保证一致性,这个时候必须拒绝请求,但是 A 又不允许,所以分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构

而且,显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择。

更多请参考  https://queue.acm.org/detail.cfm?id=1394128

1.2.  BASE理论

往往在分布式系统中无法实现完全一致性,于是有了BASE理论,它是对CAP定律的进一步扩充

BASE指的是:

  • Basically Available(基本可用) : 分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用
  • Soft state(软状态) : 允许系统中存在中间状态,这个状态不影响系统可用性
  • Eventually consistent(最终一致性) : 经过一段时间后,所有节点数据都将会达到一致

BASE理论是对CAP中的一致性和可用性进行一个权衡的结果

BASE理论核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性

BASE理论是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态

2.  分布式事务解决方案

2.1.  基于XA协议的两阶段提交

XA协议包含两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,目前主流的关系型数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。

优点:尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)

缺点:XA协议遵循强一致性。在事务执行过程中,各个节点占用着数据库资源,只有当所有节点准备完毕,事务协调者才会通知提交,参与者提交后释放资源。这样的过程有着非常明显的性能问题。

(PS:XA三阶段提交在两阶段提交的基础上增加了CanCommit阶段,并且引入了超时机制。这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。)

两阶段提交方案锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题。

2.2.  TCC方案

TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。

将整个业务逻辑的每个分支显式的分成了Try、Confirm、Cancel三个操作。Try部分完成业务的准备工作,confirm部分完成业务的提交,cancel部分完成事务的回滚。  

拿前面的下单的例子来说,服务A的try相当于查询是否有可用的积分,Confirm相当于扣减积分,Cancel相当于增加积分。

优点:跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些

缺点:TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,而且补偿的时候也有可能失败,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

2.3.  本地消息表

其基本的设计思想是将远程分布式事务拆分成一系列的本地事务。

消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。

消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。

生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。

本地消息表是一个比较好的做法,这样可以有效防止重复消息处理

以转账为例,这种方式的过程是这样的:

  1. 当A想给B转100元时,A账户先减100元,然后在其本地消息表中加一条记录,写明A给B转100元,并且状态是消息未发送。这个向消息表写数据的操作和A账户扣减100元在同一个事务中,依靠数据库本地事务保证一致性,即只要A减100成功了,这条记录必然成功。
  2. 定时任务去轮询消息表,把没有发出去的消息都发出去,并标记状态为已发送
  3. B收到这个消息的时候先存入本地的消息表,标记未处理
  4. B这边定时扫描未处理的消息,处理完成后更新状态为已处理

有了消息表以后,可以防止重复、可以重试、保证消息不丢失、做幂等性校验

优点一种非常经典的实现,避免了分布式事务,实现了最终一致性

缺点:消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

2.4.  MQ(非事务消息)

如果不把本地数据库操作和消息投递放在同一个事务中,那么很难保证本地事务成功后消息一定发送成功

如果把它们放在同一个事务中,那么考虑下面几种情况:

  • 第1种情况:本地数据库操作成功,消息发送成功,皆大欢喜
  • 第2种情况:本地数据库操作失败,消息不会发送
  • 第3种情况:本地数据库操作成功,消息发送失败(抛异常),事务回滚

以上三种情况都是正常的,不会有什么问题

然而,考虑下面这种情况:

本地数据库操作成功,消息投递成功,应用服务器挂了,事务回滚,于是不一致出现了,即本地数据库操作没成功,而消息却发成功了

如果这是转账的话,对方会无缘无故多出一笔钱

究其原因,是因为发消息不是数据库操作,它不受ACID的限制,也就是说数据库事务管不了发消息,因为他们不是同一个数据库的同一个事务,当然还有一个原因是发出去的消息是无法撤销的。(PS:在后面将要介绍的RocketMQ的事务消息可以撤销)

而上面消息表的话,是同一个数据库的同一个事务,因此不会出现这种问题

综上,这种方式有一定的风险,它无法保证本地数据库操作 与 消息投递的一致性,不建议使用

2.5.  MQ(事务消息)

目前,仅阿里云的RocketMQ支持事务消息。帮助用户实现类似 X/Open XA 的分布事务功能,通过 MQ 事务消息能达到分布式事务的最终一致。

说明:

  1. 发送方向 MQ 服务端发送消息
  2. MQ Server 将消息持久化成功之后,向发送方 ACK 确认消息已经发送成功,此时消息为半消息
  3. 发送方开始执行本地事务逻辑
  4. 发送方根据本地事务执行结果向 MQ Server 提交二次确认(Commit 或是 Rollback),MQ Server 收到 Commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 Rollback 状态则删除半消息,订阅方将不会接受该消息
  5. 在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后 MQ Server 将对该消息发起消息回查
  6. 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果
  7. 发送方根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server 仍按照步骤4对半消息进行操作

其中,事务消息发送对应步骤1、2、3、4,事务消息回查对应步骤5、6、7

更多请参见  https://help.aliyun.com/document_detail/43348.html?spm=a2c4g.11186623.6.551.2ddc47b1P5PHi2

2.6.  GTS

全局事务服务(Global Transaction Service,简称 GTS)是一款高性能、高可靠、接入简单的分布式事务中间件,用于解决分布式环境下的数据一致性问题。GTS 可以保证分布式系统中的分布式事务的 ACID 特性。它是阿里云的一款产品。

更多请参见  https://help.aliyun.com/document_detail/48726.html?spm=a2c4g.11186623.6.542.53ff681awWVYxc

3.  参考

https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html

https://www.cnblogs.com/jiangyu666/p/8522547.html

https://www.jianshu.com/p/16b1baf015e8

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 连接 连接池产生原因 连接池实现原理 小结 TEMPERANCE:Eat not to dullness;drink not to elevation.节制
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 一个优秀的工程师和一个普通的工程师的区别,不是满天飞的架构图,他的功底体现在所写的每一行代码上。-- 毕玄 1. 命名风格 【书摘】类名用 UpperCamelC
今天犯了个错:“接口变动,伤筋动骨,除非你确定只有你一个人在用”。哪怕只是throw了一个新的Exception。哈哈,这是我犯的错误。一、接口和抽象类类,即一个对象。先抽象类,就是抽象出类的基础部分,即抽象基类(抽象类)。官方定义让人费解,但是记忆方法是也不错的 —包含抽象方法的类叫做抽象类。接口
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket一、引子文件,作为常见的数据源。关于操作文件的字节流就是 —FileInputStream&FileOutputStream。
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。交流QQ群:【编程之美 365234583】http://qm.qq.com/cgi-bin/qm/qr?k=FhFAoaWwjP29_Aonqz
本文目录 线程与多线程 线程的运行与创建 线程的状态 1 线程与多线程 线程是什么? 线程(Thread)是一个对象(Object)。用来干什么?Java 线程(也称 JVM 线程)是 Java 进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。 Ja
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket在面向对象编程中,编程人员应该在意“资源”。比如?1String hello = "hello"; 在代码中,我们
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 《程序兵法:Java String 源码的排序算法(一)》 文章工程:* JDK 1.8* 工程名:algorithm-core-le
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 一、父子类变量名相同会咋样? 有个小故事,今天群里面有个人问下面如图输出什么? 我回答:60。但这是错的,答案结果是 40 。我知错能改,然后说了下父子类变
作者:泥瓦匠 出处:https://www.bysocket.com/2021-10-26/mac-create-files-from-the-root-directory.html Mac 操作系统挺适合开发者进行写代码,最近碰到了一个问题,问题是如何在 macOS 根目录创建文件夹。不同的 ma
作者:李强强上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算。这一讲,泥瓦匠带你走进Java中的进制详解。一、引子在Java世界里,99%的工作都是处理这高层。那么二进制,字节码这些会在哪里用到呢?自问自答:在跨平台的时候,就凸显神功了。比如说文件读写,数据通信,还
1 线程中断 1.1 什么是线程中断? 线程中断是线程的标志位属性。而不是真正终止线程,和线程的状态无关。线程中断过程表示一个运行中的线程,通过其他线程调用了该线程的 方法,使得该线程中断标志位属性改变。 深入思考下,线程中断不是去中断了线程,恰恰是用来通知该线程应该被中断了。具体是一个标志位属性,
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want需求 项目在设计表的时候,要处理并发多的一些数据,类似订单号不能重复,要保持唯一。原本以为来个时间戳,精确到毫秒应该不错了。后来觉得是错了,测试环境下很多一
纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 加微信:bysocket01
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want.文章Points:1、介绍RESTful架构风格2、Spring配置CXF3、三层初设计,实现WebService接口层4、撰写HTTPClient 客户
Writer :BYSocket(泥沙砖瓦浆木匠)什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道“就一个回调…”。此时千万个草泥马飞奔而过(逃哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉。不妨总结总结。一、什么是回调回调,回调。要先有调用,才有调用者和被调用者之间的回调。所以在百
Writer :BYSocket(泥沙砖瓦浆木匠)一、什么大小端?大小端在计算机业界,Endian表示数据在存储器中的存放顺序。百度百科如下叙述之:大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加
What is a programming language? Before introducing compilation and decompilation, let's briefly introduce the Programming Language. Programming la
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket泥瓦匠喜欢Java,文章总是扯扯Java。 I/O 基础,就是二进制,也就是Bit。一、Bit与二进制什么是Bit(位)呢?位是CPU
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocket一、前言 泥瓦匠最近被项目搞的天昏地暗。发现有些要给自己一些目标,关于技术的目标:专注很重要。专注Java 基础 + H5(学习) 其他操作系统,算法,数据结构当成课外书博览。有时候,就是那样你越是专注方面越