acid
原子性:记录之前的版本,允许回滚
一致性:事务开始和结束之间的中间状态不会被其他事务看到
隔离性:适当的破坏一致性来提升性能与并行度 例如:最终一致~=读未提交。
持久性:每一次的事务提交后就会保证不会丢失
1 什么是分布式事务
分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。以上是百度百科的解释,简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
2 分布式事务的产生的原因
2.1 数据库分库分表
当数据库单表一年产生的数据超过1000W,那么就要考虑分库分表,具体分库分表的原理在此不做解释,以后有空详细说,简单的说就是原来的一个数据库变成了多个数据库。这时候,如果一个操作既访问01库,又访问02库,而且要保证数据的一致性,那么就要用到分布式事务。
2.2 应用SOA化
所谓的SOA化,就是业务的服务化。比如原来单机支撑了整个电商网站,现在对整个网站进行拆解,分离出了订单中心、用户中心、库存中心。对于订单中心,有专门的数据库存储订单信息,用户中心也有专门的数据库存储用户信息,库存中心也会有专门的数据库存储库存信息。这时候如果要同时对订单和库存进行操作,那么就会涉及到订单数据库和库存数据库,为了保证数据一致性,就需要用到分布式事务。
以上两种情况表象不同,但是本质相同,都是因为要操作的数据库变多了!
4.2 实现方案
两阶段提交(2PC)
核心思想:当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件,来统一掌控所有参与节点的操作结果,并最终指示这些节点是否要把操作结果进行真正的提交。常见的协议是XA, JTA等。
整个过程如下图所示,
第一阶段:准备阶段
1. 协调者向所有的参与者发送事务执行请求,并等待参与者反馈事务执行结果。
2. 事务参与者收到请求之后,执行事务,但不提交,并记录事务日志。
3. 参与者将自己事务执行情况反馈给协调者,同时阻塞等待协调者的后续指令。
第二阶段:事务提交阶段
在第一阶段协调者的询问之后,各个参与者会回复自己事务的执行情况,这时候存在三种可能:
1. 所有的参与者回复能够正常执行事务。
2. 一个或多个参与者回复事务执行失败。
3. 协调者等待超时。
对于第一种情况,协调者将向所有的参与者发出提交事务的通知,皆大欢喜。
对于第二、三种情况,协调者均认为参与者无法正常成功执行事务,为了整个集群数据的一致性,所以要向各个参与者发送事务回滚通知。
存在的问题:
1 同步阻塞。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
2 单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
3 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
4 二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
5 柔性事务
5.1理论基础
CAP定理
理论:——在分布式系统的设计中,没有一种设计可以同时满足一致性,可用性,分区容错性 3个特性。
-
Consistency(一致性)
同一个数据在集群中的所有节点,同一时刻是否都是同样的值。
-
集群中一部分节点故障后,集群整体是否还能处理客户端的读写请求。
-
Partition tolerance(分区容忍性)
是否允许数据的分区,分区的意思是指是否允许集群中的节点之间无法通信。
描述:
对一个分布式系统而言,分区容错性是一个最基本的要求。因为既然是一个分布式系统,那么分布式系统中的组件必然需要被部署到不同的节点。 现实情况下我们面对的是一个不可靠的网络、有一定概率宕机的设备。尤其是网络问题,可以说是必然会出现异常情况,因此分区容错性也就成为了一个分布式系统必然需要面对和解决的问题。 所以对于分布式系统而言,P(分区容错)是一个必须项,而不是可选项。
因此对于分布式系统实践而言,CAP理论更合适的描述应该是:在满足分区容错的前提下,分布式系统永远不能同时满足数据一致性和服务可用性。
所以在考虑系统架构时,往往需要把精力花在如何根据业务特点在C(一致性)和A(可用性)之间寻求平衡。即如何能既保证数据一致性,又保证系统的性能
举例:?
依据目前的成功经验,可用性一般是更好的选择,而在互联网领域的绝大多数的场景,业内通常的做法都是牺牲强一致性来换取系统的高可用性。
BASE理论
此方案是 eBay 的架构师 Dan Pritchett 在 2008 年 ACM上发表文章提出。
BASE理论是对CAP理论的延伸,是对CAP理论中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。
BASE理论的核心思想是:面对大型高可用可扩展的分布式系统,即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
其核心指:
-
基本可用(Basically Available)
是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
-
软状态( Soft State)
是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
-
最终一致性( Eventual Consistency)
一致性模型
其实,数据的一致性也分几种情况,大致可以分为:
-
Strong 强一致性
新的数据一旦写入,在任意副本任意时刻都能读到新值。比如:文件系统,RDBMS都是强一致性的。
-
Weak 弱一致性
当更新操作完成之后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现需要牺牲可用性。
-
Eventually 最终一致性
弱一致性的特定形式。系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。在没有故障发生的前提下,不一致窗口的时间主要受通信延迟,系统负载和复制副本的个数影响。DNS 是一个典型的最终一致性系统。
也就是说,在设计分布式系统时,我们并不一定要求是强一致性的,根据应用场景可以选择弱一致性或者是最终一致性。
5.2 实现方案
5.2.1 补偿型
描述:提供一种基于补偿的事务处理模型。
流程:
-
do:真正执行业务
完成业务处理
业务执行结果外部可见
-
compensate:业务补偿
抵消(或部分抵消)正向业务操作的业务结果
补偿操作满足幂等性
-
约束:
补偿在业务上可行
由于业务执行结果未隔离、或者补偿不完整带来的风险和成本可控
5.2.2 TCC (Try-Confirm-Cancle)
TCC方案其实是两阶段提交的一种改进。其将整个业务逻辑的每个分支显式的分成了Try、Confirm、Cancel三个操作。它是基于补偿型事务的一种实现, 具有最终一致性。
-
Try
:阶段
完成所有的业务检查(一致性)
预留必须业务资源(准隔离性)
-
Confirm
阶段
:真正执行业务
不做任何业务检查
只使用Try阶段预留的业务资源
Confirm操作要满足幂等性
-
Cancle
阶段
:释放Try阶段预留的业务资源
Cancel操作要满足幂等性
例:支付系统支付余额积分扣减
支付系统接收到会员的支付请求后,需要扣减会员账户余额、增加会员积分(暂时假设需要同步实现)增加商户账户余额
。再假设:会员系统、商户系统、积分系统是独立的三个子系统,无法通过传统的事务方式进行处理。
-
Try阶段:我们需要做的就是会员资金账户的资金预留,即:冻结会员账户的金额(订单金额)
-
Confirm阶段:我们需要做的就是会员积分账户增加积分余额,商户账户增加账户余额
-
Cancle阶段:该阶段需要执行的就是解冻释放我们扣减的会员余额
Try
、Confirm
、Cancle
大致可以理解为sql
事务中的LOCK
、COMMIT
、rollback
。
优点
与2PC协议比较, 有两大优势:
-
位于业务服务层而非资源层。TCC能够对分布式事务中的各个资源进行分别锁定, 分别提交与释放, 例如, 假设有AB两个操作, 假设A操作耗时短, 那么A就能较快的完成自身的try-confirm-cancel流程, 释放资源. 无需等待B操作. 如果事后出现问题, 追加执行补偿性事务即可.
-
TCC是绑定在各个子业务上的(除了cancle中的全局回滚操作), 也就是各服务之间可以在一定程度上”异步并行”执行.
缺点
-
对应用的侵入性强。业务逻辑的每个分支都需要实现try、confirm、cancel三个操作,应用侵入性较强,改造成本高。
-
实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm和cancel接口必须实现幂等。
-
微服务倡导服务的轻量化、易部署,而TCC方案中很多事务的处理逻辑需要应用自己编码实现,复杂且开发量大。
适用场景
-
严格一致性
-
执行时间短
-
实时性要求高
举例: 红包, 收付款业务.
5.2.3 异步确保型(可靠消息最终一致)
通过将一系列同步的事务操作变为基于消息执行的异步操作, 避免了分布式事务中的同步阻塞操作的影响.
1.非可靠消息模式
通过消息中间件解耦,但有个问题,消息发送方同时要发送消息和本地业务操作,是先发消息还是先处理业务?
先发消息再操作db?? 两者不一致如何处理?例:消息发送成功,db操作失败,撤回来?
先操作db再发消息? 两者不一致如何处理?例: db操作成功,消息发送失败?重试?重试失败?
那这个问题怎么解决呢??
-
解决方案一
发送消息与业务操作在同一个事务域里。
-
解决方案二
消息发送方准备一张消息发送表,发送消息和业务操作在同一事务域里,同时启动一个job轮询消息表,发送成功则修改状态。允许消息重复。
2.可靠消息模式-----rocketmq
基于上面的分析,这个时候我们可能会想,如果把这个消息表,交给 MQ 自己去做,把事务消息的处理和业务逻辑解耦,岂不是很爽啊?
阿里开源的消息中间件 RocketMQ 提供了这样的机制。
(1) 发送Prepared消息
(2) update DB
(3) 根据update DB结果成功或失败,Confirm或者取消Prepared消息
RocketMQ第一阶段发送Prepared消息时,server 会拿到消息的地址,但此时的消息对 consumer 不可见;第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并根据第二阶段producer本地事务的执行情况修改消息的状态。
如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事物消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认结果?这样就保证了消息发送与本地事务同时成功或同时失败。
ok 那我们来看看阿里开源的消息中间件 RocketMQ,下面是它官方文档的说明:
-
-
事务消息:MQ 提供类似 X/Open XA 的分布事务功能,通过 MQ 事务消息能达到分布式事务的最终一致。
-
半消息:暂不能投递的消息,发送方已经将消息成功发送到了 MQ 服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半消息。
-
消息回查:由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,MQ 服务端通过扫描发现某条消息长期处于“半消息”时,需要主动向消息生产者询问该消息的最终状态(Commit 或是 Rollback),该过程即消息回查。
-
MQ 事务消息交互流程如下所示:
-
发送方向 MQ 服务端发送消息;
-
MQ Server 将消息持久化成功之后,向发送方 ACK 确认消息已经发送成功,此时消息为半消息。
-
发送方开始执行本地事务逻辑。
-
发送方根据本地事务执行结果向 MQ Server 提交二次确认(Commit 或是 Rollback),MQ Server 收到 Commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 Rollback 状态则删除半消息,订阅方将不会接受该消息。
-
在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后 MQ Server 将对该消息发起消息回查。
-
发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
-
发送方根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server 仍按照步骤4对半消息进行操作。
相关阅读
最近读了下raincat,hmily的源码,也看了ByteTcc,tcc-transaction,lcn等框架的比较。但是对于技术选型而言,还是希望学习下BAT这种体量的
NoSQL-Tidis支持分布式事务,兼容redis协议,使用tikv存储
项目repo地址 https://github.com/yongman/tidis Tidis是分布式数据库,支持redis协议,多种数据结构支持,编写语言为golang。 Tidis
事务管理(ACID) 谈到事务一般都是以下四点原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不
1)原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;2)一致性(Consistent):事务结束后系