目录
一. 分布式事务存在两大理论依据
二. 当下流行的分布式事务解决方案
1. TX-LCN
LCN(lock-confirm-notify)原理
TCC(try-confirm-cancel) - 原理
TXC(taobao-transaction-constructor)- 原理
2. Seata(GTS开源版本)
三. 分布式锁
分布式锁的实现
何情况下用分布式锁
四. 分布式事务
五. 分布式事务选型
1. CAP定律, 指的是在一个分布式系统中、Consistency(强一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
一致性(C-Consistency):在分布式系统中所有的数据备份,在同一时刻是否有同样的值(强一致性).
可用性(A-Availability):即服务一直可用,而且是正常响应时间。好的可用性主要是指系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况。
分区容错性(P-Partition tolerance):以实际效果而言,分区相当于对通信时限的要求.系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A做出抉择(分区状态可理解为部分机器不连通,如机器挂了或单机房故障),而且在分布式系统中,P是必选项,而不是可选项.
2. BASE理论, 是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
基本可用:是指分布式系统在出现不可预知故障的时候,允许损失部分可用性. Note: 这绝不等价于系统不可用。比如:
a. 响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒
b. 系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
软状态:指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时
最终一致性:最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
TX-LCN定位于一款事务协调性框架,框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果;LCN并不产生事务,LCN只是事务的搬运工.
TX-LCN包含两个模块:Tx-Client(TC) Tx-Manager(TM).TC即作为微服务下的依赖,TM则是独立的服务.
核心步骤:
- 创建事务组, 是指在事务发起方在调用业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupID的过程.
- 加入事务组,添加事务组是指参与方在执行完业务方法之后,将该模块的事务信息通知给TxManager的操作.
- 通知事务组,是指在发起方执行完业务方法之后,将发起方执行结果的状态通知给TxManager,TxManager根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方.
- LCN事务控制原理是由事务模块TxClient下的代理连接池与TxManager的协调配合完成的.TxClient的代理连接池实现了javax.sql.DataSource接口,并重写了close方法,事务模块在提交关闭以后,TxClinet连接池将执行"假关闭"操作,等待TxManager协调完成事务以后在真正关闭连接.
- 优缺点
1. 性能优秀,可靠性强.
2. 兼容性强,支持所有的关系型数据库事务,支持多数据源.
3. 业务入侵性低,使用时只需添加相应注解即可.
4. 需额外部署Tx-Manager节点,增加相应的运维成本.
5. 由于需要lock资源这种处理方式,在更新某几个热点商品时LCN的性能衰减量大于TCC模型.
6. 服务超时时,会造成其他服务的资源被锁住.如支付服务超时,相关商品的库存会一直无法操作.
- TCC事务机制相对于传统事务机制(XA-2PC),他不依赖于资源管理器(RM)对XA的支持,而是通过对业务逻辑的调度来实现的分布式事务.主要分为三个步骤:Try-尝试执行业务,Confirm-确认执行业务,Cancel-取消执行业务.
- 优缺点
1. 该模式代码侵入性极高,需要每个业务写三个步骤的操作.
2. 数据的一致性几乎完全由开发者控制,对业务开发要求难度高.
3. 该模式对本地有无事务都可以支持.
命名来源于淘宝中间件团队,实现原理是在执行SQL之前,先查询SQL影响的数据,然后保存执行的SQL快照信息和创建锁.当需要回滚时就采用这些
- 优缺点
1. 代码侵入性低.
2. 该模型仅限于对支持SQL的模块支持.
3. 该模式由于在每次执行SQL之前都要查询影响的数据,因此消耗的资源与时间要比LCN模式要多.
4. 该模式不会占用数据库的连接资源.
2014 年,阿里中间件团队发布 **TXC(Taobao Transaction Constructor)**,为集团内应用提供分布式事务服务. 2016 年,TXC 经过产品化改造,以 **GTS(Global Transaction Service)** 的身份登陆阿里云,成为当时业界唯一一款云上分布式事务产品,在阿云里的公有云、专有云解决方案中,开始服务于众多外部客户. 2019 年起,基于 TXC 和 GTS 的技术积累,阿里中间件团队发起了开源项目 **Fescar(Fast & EaSy Commit And Rollback, FESCAR)**,后更名为Seata。
Seata分布式事务方案:为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
Saga事务模型又叫做长时间运行的事务(Long-running-transaction), 它描述的是另外一种在没有两阶段提交的的情况下解决分布式系统中复杂的业务事务问题。该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas 工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。
过程解析:
1. TM先向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID.
2. XID在服务调用的链路上下文中传播.
3. RM向TC注册分支事务,将其纳入XID对应的全局事务的管辖.
4. TM向TC发起针对XID的全局提交或回滚决议.
5. TC调度XID下管辖的全部分支事务完成提交或回滚请求.
优缺点(包含GTS优缺点):
1. 高性能、高可靠、接入简单,可做到业务代码代码零入侵
2. 支持多种数据源
3. 兼容多种微服务框架
4. GTS无法在本地测试,只能在阿里云内网使用,或者购买专有云
5. 当前Seata未达到生产可用版本
Seata 在 AT 模式下稍微有点代码量的地方就是对数据源的代理指定
使用 DataSourceProxy 的目的是为了引入 ConnectionProxy ,Seata 无侵入的一方面就体现在 ConnectionProxy 的实现上,
即分支事务加入全局事务的切入点是在本地事务的 commit 阶段,这样设计可以保证业务数据与 undo_log 是在一个本地事务中.
用例: 用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
- 存储服务:扣除给定商品的存储数量。
- 订单服务:根据购买请求创建订单。
- 帐户服务:借记用户帐户的余额。
在使用Seata的前提下,实现分布式事物只需将 @GlobalTransactional 加在业务方法上即可.
为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。
分布式锁应该具备哪些条件
在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
高可用的获取锁与释放锁,高性能的获取锁与释放锁,具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)。
具备锁失效机制,防止死锁。
具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
Memcached利用 Memcached 的 add 命令。此命令是原子性操作,只有在 key 不存在的情况下,才能 add 成功,也就意味着线程得到了锁。
Redis和 Memcached 的方式类似,利用 Redis 的 setnx 命令。此命令同样是原子性操作,只有在 key 不存在的情况下,才能 set 成功。
Zookeeper利用 Zookeeper 的顺序临时节点,来实现分布式锁和等待队列。Zookeeper 设计的初衷,就是为了实现分布式锁服务的。
Chubby,Google公司实现的粗粒度分布式锁服务,底层利用了 Paxos 一致性算法。
成员变量 A 存在 JVM1、JVM2、JVM3 三个 JVM 内存中
成员变量 A 同时都会在JVM分配一块内存,三个请求发过来同时对这个变量操作,显然结果是不对的
不是同时发过来,三个请求分别操作三个不同 JVM 内存区域的数据,变量 A 之间不存在共享,也不具有可见性,处理的结果也是不对的
注:该成员变量 A 是一个有状态的对象
如果我们业务中确实存在这个场景的话,我们就需要一种方法解决这个问题,这就是分布式锁要解决的问题。
当我们在进行下订单减库存,抢票,选课,抢红包这些业务场景时,如果在此处没有锁的控制,会导致很严重的问题。学过多线程的小伙们知道,为了防止多个线程同时执行同一段代码,我们可以用 synchronized 关键字或 JUC 里面的 ReentrantLock 类来控制,但是目前几乎任何一个系统都是部署多台机器的,单机部署的应用很少,synchronized 和 ReentrantLock 发挥不出任何作用,此时就需要一把全局的锁,来代替 JAVA 中的 synchronized 和 ReentrantLock。
事务实现应该是具备原子性Atomicity、一致性Consistency、隔离性Isolation和持久性Durability,简称 ACID。
2PC(Two-phase commit protocol),叫二阶段提交。二阶段提交是一种强一致性设计,2PC引入一个事务协调者的角色来协调管理各参与者(也可称之为各本地资源)的提交和回滚,二阶段分别指的是准备(投票)和提交两个阶段。
是一种尽量保证强一致性的分布式事务,因此它是同步阻塞的,而同步阻塞就导致长久的资源锁定问题,总体而言效率低,并且存在单点故障问题,在极端条件下存在数据不一致的风险。适用于数据库层面的分布式事务场景。基于XA规范实现的事务接口。
3PC的出现是为了解决 2PC 的一些问题,相比于 2PC 它在参与者中也引入了超时机制,并且新增了一个阶段使得参与者可以利用这一个阶段统一各自的状态。 包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段,对应的英文就是:CanCommit、PreCommit 和 DoCommit。是数据库层面的。
3PC 相对于 2PC 做了一定的改进:引入了参与者超时机制,并且增加了预提交阶段使得故障恢复之后协调者的决策复杂度降低,但整体的交互过程更长了,性能有所下降,并且还是会存在数据不一致问题。
2PC和3PC都不能保证数据100%一致,因此一般都需要有定时扫描补偿机制。是一种强一致性事务,不过还是有数据不一致,阻塞等风险,而且只能用在数据库层面。
TCC是业务层面的分布式事务,不仅仅包括数据库的操作,还包括发送短信等,TCC指的是Try - Confirm - Cancel。 Try 指的是预留,即资源的预留和锁定,注意是预留。Confirm 指的是确认操作,这一步其实就是真正的执行了。Cancel 指的是撤销操作,可以理解为把预留阶段的动作撤销了。
难点在于业务上的定义,对于每一个操作你都需要定义三个动作分别对应Try - Confirm - Cancel。因此TCC 对业务的侵入较大和业务紧耦合,需要根据特定的场景和业务逻辑来设计相应的操作。还有一点要注意,撤销和确认操作的执行可能需要重试,因此还需要保证操作的幂等。
相对于 2PC、3PC ,TCC 适用的范围更大,但是开发量也更大,毕竟都在业务上实现,而且有时候你会发现这三个方法还真不好写。不过也因为是在业务上实现的,所以TCC可以跨数据库、跨不同的业务系统来实现事务。
选型依据:
- 多语言支持
- 微服务框架兼容程度
- 关系型数据库-MQ事务的支持
- 性能与稳定性以及是否支持高可用
- 业务代码侵入性
- 可拓展性
- 社区活跃度及影响力