Skip to content

分布式问题——事务问题

更新: 4/26/2025 字数: 0 字 时长: 0 分钟

推荐阅读——小米信息部技术团队:分布式事务,这一篇就够了

当我们将单体应用拆分为分布式应用后,会自然的出现很多的问题,其中最常见的就是分布式事务问题

前置知识

事务

所谓事务也就是一系列严密的操作,这些操作要么同时完成,要么都不完成,这种操作一般具有以下属性:原子性,一致性,隔离性与持久性,也就是我们常说的ACID

场景

当我们去对数据库进行一次CRUD操作,那么很有可能会失败,这时SpringBoot为我们提供的事务注解(@Transaction)就可以在代码层面帮助我们完成回滚

但是在微服务场景下,由于不同的业务与库表可能部署在不同的服务器上,那么我们就不可能仅依靠一个注解就对完成事务

这时我们必须考虑其他的解决方案

三种一致性

  • 强一致性:任何时刻下所有节点的数据完全一致
  • 弱一致性:在一个时刻下,允许部分节点的数据不一致
  • 最终一致性:不在追求一个时刻下的数据一致,仅要求在一段时间后数据可以一致

不难看出三种一致性的容忍度是逐渐加大的

使用微服务的代价和他使用他的好处一样的明显,其中一个代价就是一致性问题,较长时间的最终一致性会导致一个事件发布后只有在一段时间之后才能被读到,这种情况对于一些应用和业务来说是几乎无法容忍的

CAP原则

Martin Fowler曾经在他的个人博客中提到过,微服务并不适合所有的软件开发情景。微服务的代价和它的好处一样明显。

Eric Brewer在1998年提出微服务有三个指标(CAP),而这三个指标是不可能同时做到的。

  • Consistency(C,一致性)
  • Availability(A,可用性)
  • Partition tolerance(P,分区容错)

Partition tolerance

分布式系统分布在多个子网络中,每个子网络都叫做一个区(Partition),而分区容错就是说允许不同的分区可能会无法通信

由于这种情况是无法避免的,因此P总是成立,CAP原则在更多的情况下是考虑A和P选择哪一个

Consistency

一致性我们上方已经提及过一些,这里指的其实是强一致性

假设我们将一个服务部署在两台服务器上,这时我们向A服务器推送一篇文章,这时B服务是完全读不到这篇文章的,因此我们需要在向A服务推送文章后再想办法向B服务推送一次,保证两台服务器间的数据一致

Availability

所谓可用性就是说用户一旦发起请求那么服务器应该给出相应。

C和A的问题

C和A之间的问题也就导致了我们之前提到的不同的一致性,这里说一下发生的原因,

如果我们想要完成强一致性,那么A服务器在进行写操作的时候B服务器应该拒绝接受一切对该内容的读写操作,只有等待数据同步完成后才能开发读写操作,这就导致了无法保证所有节点的可用性

BASE理论

BASE 理论指的是基本可用 Basically Available,软状态 Soft State,最终一致性 Eventual Consistency,核心思想是即便无法做到强一致性,但应该采用适合的方式保证最终一致性。

  • 基本可用:允许部分可以可用性可以损失,但核心业务要保证可用
  • 软状态:允许纯在一个中间状态,这个中间状态不会影响系统的整体可用
  • 最终一致性:牺牲强一致性,仅要求最终一致

分布式事务解决方案

XA模式/两阶段提交

XA模式中,用户的一个请求不会直接的操作各个资源,而是先于TM(Transaction Manager,事务管理器)进行交互,再由TM去统一管理操作RM(Resource Manager,资源管理器,一般就是数据库的驱动,消息队列客户端等)

  1. 第一阶段:TM会向RM发起一个准备通知,让RM去检测当前的任务是否能够成功完成,RM检测完成后会将该操作设计的所有修改持久化到日志中,然后启动一个锁防止其他事务的干扰,然后返回给TM一个是否能够完成的回复
  2. 第二阶段:TM在获取了所有的通知后,会向RM要求完成回滚(存在至少一个RM返回无法完成)或是提交(所有RM均回复可以完成)操作
    • 提交:RM会正式将应用第一阶段持久化的操作并释放掉锁
    • 回滚:RM会撤销之前的所有临时修改并释放掉锁

当所有的RM均完成后,TM认为本次的事务已经完成

缺点:

  • 同步阻塞:由于要上锁,因此使用统一资源的其他事务必须等待当前事务完成后才能操作
  • 单点故障:依赖事务管理器,若事务管理器出现问题则无法生效

Apache Seata的AT模式就是对XA模式的一种实现与优化

TCC模式/Try,Confirm,Cancel

TCC不使用一个TM而是使用一个协调者来完成整个的操作,协调者可能是一个独立的微服务也可能集成在你的代码中(因此TCC相对XA具有较高的侵入性),他负责管理TCC的整个事务周期并完成整个TCC事务

TCC要求微服务还要提供一个Try接口用于进行尝试,只有Try接口全部成功才能进行Confirm

  1. Try:在该阶段,协调者会调用其他微服务提供的Try接口,微服务们通过Try接口中的逻辑来判断当前任务是否可以完成,并将结果返回给协调者
  2. 当协调者收到了所有的返回内容,决定是进行Confirm(Try全部成功)还是Cancel(Try存在至少一个不成功)
    • Confirm:协调者向所有的微服务进行Commit要求,微服务将Try的内容正式同步为Commit后的状态
    • Cancel:协调者向所有的微服务进行Cancel要求,微服务将Try的内容回滚到Try之前

优点:

  • 不再存在一个锁,因此不会阻塞,效率更高
  • 协调者可以部署多个,因此不再出现单点故障

缺点:

  • 高侵入性
  • 不保证强一致性

Apache Seata和Dromara hmily提供了TCC的实现方案

本站访客数 人次      本站总访问量