5.7 分布式事务
5.7.1 什么是分布式事务?
分布式事务是分布式架构下的一种事务处理方式,是指位于不同的节点之上的事务参与者,执行一系列操作时,要确保这些操作要么都执行成功,要么都执行失败。以保证数据的一致性和完整性。
传统的单机事务(例如在关系型数据库中使用的ACID事务)可以通过数据库的事务管理机制来实现,但在分布式环境中,涉及到多个独立的服务或数据库实例,无法使用单一的事务管理机制来完成分布式事务的管理。因此,需要使用特殊的技术和方法来实现分布式事务。
5.7.2 CAP定理及对分布式事务的影响?
在分布式系统中,有三个指标分别是:
- 一致性(Consistency)是指在分布式系统中的多个副本或节点之间,无论用户如何进行读取操作或更新操作,最终都能保证数据的一致性。即系统要么返回最新的数据(强一致性),要么返回过去一段时间内的最新数据(最终一致性)。
- 可用性(Availability):是指在分布式系统中,系统能够持续地保持对外提供服务,即使在某些节点或副本出现故障的情况下也能够继续正常运行。
- 分区容错性(Partition tolerance)是指在分布式系统中,系统能够正常工作并保持一致性或可用性,即使在网络中存在分区通讯中断的情况下。这也是分布式架构系统中必须要满足的一个指标,在分布式架构下的一台服务器出了问题,其它服务必须依旧可以持续提供业务服务。
这三个指标不可能同时满足,这个定理就叫CAP定理。在分布式系统中,通常分区容错性是必须满足的,可用性(AP)和一致性(CP)只能选择其一。
因此,在设计和实现分布式系统并进行分布式事务处理时,需要根据具体的业务需求和系统的特点,权衡在一致性、可用性和分区容错性之间的取舍。常见的做法是在系统中进行适当的折中,根据业务的重要性和数据的一致性要求,选择合适的分布式架构和技术方案。
5.7.3 解释一下什么是BASE理论?
BASE理论是对分布式系统设计原则的一个概括。BASE是指:基本可用(Basically Available)、软状态(Soft state)和最终一致性(Eventually Consistent)。
基本可用性(Basically Available)是指在分布式系统中,尽管系统可能会遇到故障或部分节点不可用的情况,但系统仍然能够保持基本的可用性,即能够继续处理和响应用户的请求。
软状态(Soft state)是指在分布式系统中的节点之间,并不要求数据的强一致性,允许存在一段时间的数据不一致,即允许数据的状态是“软”的。这样的设计思路可以提高系统的可用性和性能。
最终一致性(Eventually Consistent)是指在分布式系统中,虽然数据会存在一定的时间窗口内的不一致性,但最终数据会达到一致的状态。系统会通过一定的机制,例如异步复制和定时同步等,保证数据的最终一致性。
BASE理论相对于ACID(原子性、一致性、隔离性和持久性)事务模型,提供了一种更加宽松和灵活的设计思路,可以满足分布式系统中的高可用性和性能要求。在BASE理论中,系统设计者可以根据对系统的要求和业务场景,合理地权衡一致性和性能,选择适合的一致性模型。
💡需要注意的是,BASE理论并不是说一致性不重要,而是在实际的分布式系统中,根据业务需求和系统特点,通过一系列的机制和策略来实现最终一致性。这样既可以保证系统的可用性,又可以满足用户对数据一致性的要求。
5.7.4 解释一下两阶段提交协议?
两阶段提交是一种分布式事务处理的协议。它包含两个阶段:准备阶段和提交阶段。在准备阶段,协调者节点向所有参与者节点发送要执行的事务,并询问它们是否准备好提交事务。参与者节点会执行事务并在准备好的情况下向协调者发送“准备就绪”消息。在提交阶段,协调者(Coordinator)节点根据参与者节点的反馈来决定是提交还是回滚事务。
当商城应用订单创建时,首先事务协调者会向各服务下达“处理本地事务”的通知,所谓本地事务就是每个服务应该做的事情,如订单服务中负责创建新的订单记录;会员服务负责增加会员的积分;库存服务负责减少库存数量。在这个阶段,被操作的所有数据都处于未提交(uncommit)的状态,会被排它锁锁定。这个阶段也是二阶段提交中的第一阶段(也称之为预处理阶段) ,如图所示:
当本地事务都处理完成后,会通知事务协调者“本地事务处理完毕”。当事务协调者陆续收到订单、会员、库存服务的处理完毕通知后,便进入“阶段二:提交阶段”。
在提交阶段,事务协调者会向每一个服务下达提交命令,每个服务收到提交命令后在本地事务中对阶段一未提交的数据执行 Commit 提交以完成数据最终的写入,之后服务便向事务协调者上报“提交成功”的通知。当事务协调者收到所有服务“提交成功”的通知后,就意味着一次分布式事务处理已完成。
这便是二阶段提交的正常执行过程,但假设在阶段一有任何一个服务因某种原因向事务协调者上报“事务处理失败”,就意味着整体业务处理出现问题,阶段二的操作就自动改为回滚(Rollback)处理,将所有未提交的数据撤销,使数据还原以保证完整性。
5.7.5 分布式事务二阶段提交有什么缺陷?
两阶段提交协议存在阻塞问题,即如果任何一个参与者节点或协调者节点宕机或网络中断,整个协议会处于阻塞状态。此外,两阶段提交协议在故障恢复和扩展方面也存在一些复杂性和性能开销。
以上图为例,假如在提交阶段,库存服务实例与事务协调者之间断网。提交指令无法下达,这会导致商品库存记录会长期处于未提交的状态,因为这条记录被数据库排他锁长期独占,之后再有其他线程要访问“飞科剃须刀”库存数据,该线程就会长期处于阻塞状态,随着阻塞线程的不断增加,库存服务会面临崩溃的风险。
那这个问题要怎么解决呢?其实只要在服务这一侧增加超时机制,过一段时间被锁定的“飞科剃须刀”数据因超时自动执行提交操作,释放锁定资源。尽管这样做会导致数据不一致,但也比线程积压导致服务崩溃要好,出于此目的,三阶段提交(3PC)便应运而生。
5.7.6 分布式事务中的三阶段提交?
三阶段提交实质是将二阶段中的提交阶段拆分为“预提交阶段”与“提交阶段”,同时在服务端都引入超时机制,保证数据库资源不会被长时间锁定。下面是三阶段提交的示意流程:
阶段1:事务预处理阶段,3PC 的事务预处理阶段与 2PC 是一样的,用于处理本地事务,锁定数据库资源,例如:
阶段2:当所有服务返回成功后,进入阶段二。预提交阶段只是一个询问机制,以确认所有服务都已准备好,同时在此阶段协调者和参与者都设置了超时时间以防止出现长时间资源锁定。
当阶段二所有服务返回“可以提交”,进入阶段三“提交阶段”。
3PC 的提交阶段与 2PC 的提交阶段是一致的,在每一个数据库中执行提交实现数据的资源写入,如果协调者与服务通信中断导致无法提交,在服务端超时后在也会自动执行提交操作来保证资源释放。
三阶段提交本质上是二阶段提交的优化版本,主要通过加入预提交阶段引入了超时机制,让数据库资源不会被长期锁定,但这也会带来一个新问题,数据一致性也很可能因为超时后的强制提交被破坏,对于这个问题各大软件公司都在各显神通,常见的做法有:增加异步的数据补偿任务、更完善的业务数据完整性的校验代码、引入数据监控及时通知人工补录这些都是不错的补救措施。
“Transaction Coordinator (TC)” 常见释义为 事务协调器 。在计算机分布式系统领域,事务协调器是用于管理分布式事务的组件 。它负责协调分布式环境中各个参与者(如不同的数据库节点、服务等)之间的操作,确保事务的原子性、一致性、隔离性和持久性(ACID特性) 。比如在微服务架构中,多个服务可能需要协同完成一个业务操作,事务协调器会控制这些服务的操作流程,要么全部成功提交,要么全部回滚,以保证数据的一致性。
Transaction Manager ™ 即 事务管理器 。在分布式系统里,它起着关键作用:
- 控制事务边界:界定一个分布式事务从开始到结束的范围。比如在电商系统中,下单操作涉及库存扣减、支付处理等多个跨服务操作,事务管理器确定这些操作都在同一个事务范畴内,哪些操作开始纳入事务管理,哪些操作完成后事务结束。
- 发起提交/回滚请求 :当事务中的所有操作都成功执行时,事务管理器发起提交请求,将各个参与者的操作结果持久化;若其中某个操作失败,事务管理器则发起回滚请求,让所有参与者撤销已执行的操作,保证数据状态回到事务开始前,维持数据一致性。
Resource Manager (RM) 即 资源管理器 。在分布式事务场景中:
- 管理本地资源:负责管理如数据库连接、文件系统资源等本地资源。例如在数据库场景下,RM 掌控数据库连接的创建、分配和释放,对数据库事务进行管理,包括执行 SQL 语句、维护事务状态等。
- 与 TC 和 TM 通信:当接收到来自事务协调器(TC)和事务管理器(TM)的指令时,RM 根据指令执行相应操作。收到提交指令,RM 将本地事务的操作结果持久化;收到回滚指令,则撤销本地事务已执行的操作,以此保障分布式事务中本地事务与全局事务的一致性。
在JDBC编程中,我们通过java.sql.Connection对象来开启、关闭或者提交事务。代码如下所示:
Connection conn = ... //获取数据库连接
conn.setAutoCommit(false); //开启事务
try{
//...执行增删改查sql
conn.commit(); //提交事务
}catch (Exception e) {
conn.rollback();//事务回滚
}finally{
conn.close();//关闭链接
}
假设你在一个连锁超市工作,顾客A在北京买了瓶水,然后又跑到上海买了个面包。问题来了——两个城市的收银系统要同步:账上得记着人家消费了两样东西,库存也得扣掉。可是呢,北京和上海的系统数据经常对不上,要么少算,要么多算。这种跨地域、跨系统的协调问题就是分布式事务的“职场原型”。
对于分库分表的情况,一般开发人员都会使用一些数据库中间件来降低sql操作的复杂性。
如,对于sql:
insert into user(id,name) values (1,"张三"),(2,"李四")
这条sql是操作单库的语法,单库情况下,可以保证事务的一致性。
但是由于现在进行了分库分表,开发人员希望将1号记录插入分库1,2号记录插入分库2。
所以数据库中间件要将其改写为2条sql,分别插入两个不同的分库,此时要保证两个库要不都成功,要不都失败。
因此基本上所有的数据库中间件都面临着分布式事务的问题。
微服务架构
下图演示了一个3个服务之间彼此调用的微服务架构:
Service A完成某个功能需要直接操作数据库,同时需要调用Service B和Service C。
而Service B又同时操作了2个数据库,Service C也操作了一个库。
需要保证这些跨服务调用对多个数据库的操作要么都成功,要么都失败,实际上这可能是最典型的分布式事务场景。
小结:
上述讨论的分布式事务场景中,无一例外的都直接或者间接的操作了多个数据库。如何保证事务的ACID特性,对于分布式事务实现方案而言,是非常大的挑战。同时,分布式事务实现方案还必须要考虑性能的问题,如果为了严格保证ACID特性,导致性能严重下降,那么对于一些要求快速响应的业务,是无法接受的。
最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。
这是大佬写的 7701页的BAT大佬写的刷题笔记,让我offer拿到手软
1. 经典解决方案剖析:两阶段提交协议(2PC)
两阶段提交方案下全局事务的ACID特性,是依赖于RM的。
一个全局事务内部包含了多个独立的事务分支,这一组事务分支要么都成功,要么都失败。
各个事务分支的ACID特性共同构成了全局事务的ACID特性。也就是将单个事务分支支持的ACID特性提升一个层次到分布式事务的范畴。
2PC存在的问题
同步阻塞问题
2PC 中的参与者是阻塞的。在第一阶段收到请求后就会预先锁定资源,一直到 commit 后才会释放。
-
单点故障
由于协调者的重要性,一旦协调者TM发生故障,参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。 -
数据不一致
若协调者第二阶段发送提交请求时崩溃,可能部分参与者收到commit请求提交了事务,而另一部分参与者未收到commit请求而放弃事务,从而造成数据不一致的问题。
2. 经典方案剖析:Seata 分布式事务
Seata是什么?
用官方的话说就是:
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务)
通俗的解释就是:
Seata 就是帮你管理多个服务干活的“事务管家”。它有两套大杀器:AT 模式和 TCC 模式。
别被这些字母吓到,它们干的活,说白了就像处理公司财务报销——一种是“帮你自动对账”,另一种是“让你每步都签字画押”。
2.1 AT 模式:低侵入的“自动对账”高手
以用户下单为例
try-commit
try 阶段首先进行预留资源,然后在 commit 阶段扣除资源。如下图:
try-cancel