如何保障微服务架构下的数据⼀致性
1、微服务架构的数据⼀致性问题
以电商平台为例,当⽤户下单并⽀付后,系统需要修改订单的状态并且增加⽤户积分。由于系统采⽤的是微服务架构,分离出了⽀付服务、订单服务和积分服务,每个服务都有独⽴数据库做数据存储。当⽤户⽀付成功后,⽆论是修改订单状态失败还是增加积分失败,都会造成数据的不⼀致。
为了解决例⼦中的数据⼀致性问题,⼀个最直接的办法就是考虑数据的强⼀致性。那么如何保证数据的强⼀致性呢?我们从关系型数据库的ACID 理论说起。
关系型数据库具有解决复杂事务场景的能⼒,关系型数据库的事务满⾜ ACID 的特性。
Atomicity:原⼦性(要么都做,要么都不做)
Consistency:⼀致性(数据库只有⼀个状态,不存在未确定状态)
Isolation:隔离性(事务之间互不⼲扰)
Durability:永久性(事务⼀旦提交,数据库记录永久不变)
具有 ACID 特性的数据库⽀持数据的强⼀致性,保证了数据本⾝不会出现不⼀致。
然⽽微服务架构下,每个微服务都有⾃⼰的数据库,导致微服务架构的系统不能简单地满⾜ ACID,我们就需要寻微服务架构下的数据⼀致性解决⽅案。
微服务架构的系统本⾝是⼀种分布式系统,⽽本⽂讨论的问题其实也就是分布式事务之数据⼀致性的问题,我们来聊聊分布式系统的 CAP 理论和 BASE 理论。
CAP 是指在⼀个分布式系统下,包含三个要素:Consistency(⼀致性)、Availability(可⽤性)、Partition tolerance(分区容错性),并且三者不可得兼。
C:Consistency,⼀致性,所有数据变动都是同步的。
A:Availability,可⽤性,即在可以接受的时间范围内正确地响应⽤户请求。
P:Partition tolerance,分区容错性,即某节点或⽹络分区故障时,系统仍能够提供满⾜⼀致性和可⽤性的服务。
关系型数据库单节点保证了数据强⼀致性(C)和可⽤性(A),但是却⽆法保证分区容错性(P)。
然⽽在分布式系统下,为了保证模块的分区容错性(P),只能在数据强⼀致性(C)和可⽤性(A)之间做平衡。具体表现为在⼀定时间内,可能模块之间数据是不⼀致的,但是通过⾃动或⼿动补偿后能够达到最终的⼀致。
BASE 理论主要是解决 CAP 理论中分布式系统的可⽤性和⼀致性不可兼得的问题。BASE 理论包含以下三个要素:
BA:Basically Available,基本可⽤。
分布式和微服务的关系
S:Soft State,软状态,状态可以有⼀段时间不同步。
E:Eventually Consistent,最终⼀致,最终数据是⼀致的就可以了,⽽不是时时保持强⼀致。
BASE 模型与 ACID 不同,满⾜ CAP 理论,通过牺牲强⼀致性来保证系统可⽤性。由于牺牲了强⼀致性,系统在处理请求的过程中,数据可以存在短时的不⼀致。
系统在处理业务时,记录每⼀步的临时状态。当出现异常时,根据状态判断是否继续处理请求或者退回原始状态,从⽽达到数据的最终⼀致。
例如,在上⾯的案例中,⽀付成功,订单也成功,但增加积分失败,此时,不应回滚⽀付和订单,⽽应通过⼀些补偿⽅法来让积分得以正确地增加。后⾯会讲到具体的实现⽅法。
在分享我们的分布式事务实践⽅案之前,先看看早期解决分布式事务问题的⼆阶段提交协议。
2、⼆阶段提交协议
X/Open DTP(Distributed Transaction Process)是⼀个分布式事务模型,此模型主要使⽤⼆阶段提交(2PC,Two-Phase-Commit)来保证分布式事务的完整性。在这个模型⾥⾯,有三个⾓⾊:
AP:Application,应⽤程序,业务层。
RM:Resource Manager,资源管理器,关系型数据库或⽀持 XA 接⼝(XA 规范是 X/Open 组织定义的分布式事务规范)的组件。
TM: Transaction Manager ,事务管理器,负责各个 RM 的提交和回滚。
当应⽤程序(AP)调⽤了事务管理器(TM)的提交⽅法时,事务的提交分为两个阶段实⾏。
2.1、第⼀阶段(准备阶段)
TM 通知所有参与事务的各个 RM,给每个 RM 发送 prepare 消息。
RM 接收到消息后进⼊准备阶段后,要么直接返回失败,要么创建并执⾏本地事务,写本地事务⽇志(redo 和 undo ⽇志),但是不提交(此处只保留最后⼀步耗时最少的提交操作给第⼆阶段执⾏)。
2.2、第⼆阶段(提交 / 回滚阶段)
TM 收到 RM 准备阶段的失败消息或者获取 RM 返回消息超时,则直接给 RM 发送回滚(rollback)消息,否则发送提交(commit)消息。RM 根据 TM 的指令执⾏提交或者回滚,执⾏完成后释放所有事务处理过程中使⽤的锁(最后阶段释放锁)。
2.3、⼆阶段提交的利弊
优点
2PC 提供了⼀套完整的分布式事务的解决⽅案,遵循事务严格的 ACID 特性。
缺点
TM 通过 XA 接⼝与各个 RM 之间进⾏数据交互,从第⼀阶段的准备阶段,业务所涉及的数据就被锁定,并且锁定跨越整个提交流程。
在⾼并发和涉及业务模块较多的情况下对数据库的性能影响较⼤。
⼆阶段是反可伸缩模式的,业务规模越⼤,涉及模块越多,局限性越⼤,系统可伸缩性越差。
在技术栈⽐较杂的分布式应⽤中,存储组件有很多不⽀持 XA 协议。
⼆阶段的诸多弊端,导致分布式系统下⽆法直接使⽤此⽅案来解决数据⼀致性问题,但它提供了解决分布式系统下数据⼀致性问题的思路。。
下⾯就通过案例来分享我们是如何保证微服务架构的数据⼀致性的。
3、可靠消息最终⼀致性
可靠消息最终⼀致性⽅案本质上是利⽤ MQ 组件实现的⼆阶段提交。此⽅案涉及 3 个模块:
上游应⽤,执⾏业务并发送 MQ 消息。
可靠消息服务和 MQ 消息组件,协调上下游消息的传递,并确保上下游数据的⼀致性。
下游应⽤,监听 MQ 的消息并执⾏⾃⾝业务。
3.1、上游应⽤执⾏业务并发送 MQ 消息(第⼀阶段)
上游应⽤将本地业务执⾏和消息发送绑定在同⼀个本地事务中,保证要么本地操作成功并发送 MQ 消息,要么两步操作都失败并回滚。
上游应⽤和可靠消息之间的业务交互图如下:
1. 上游应⽤发送待确认消息到可靠消息系统
2. 可靠消息系统保存待确认消息并返回
3. 上游应⽤执⾏本地业务
4. 上游应⽤通知可靠消息系统确认业务已执⾏并发送消息。
5. 可靠消息系统修改消息状态为发送状态并将消息投递到 MQ 中间件。
以上每⼀步都可能出现失败情况,分析⼀下这 5 步出现异常后上游业务和消息发送是否⼀致:
上游应⽤执⾏完成,下游应⽤尚未执⾏或执⾏失败时,此事务即处于 BASE 理论的 Soft State 状态。
3.2、下游应⽤监听 MQ 消息并执⾏业务(第⼆阶段)
下游应⽤监听 MQ 消息并执⾏业务,并且将消息的消费结果通知可靠消息服务。
可靠消息的状态需要和下游应⽤的业务执⾏保持⼀致,可靠消息状态不是已完成时,确保下游应⽤未执⾏,可靠消息状态是已完成时,确保下游应⽤已执⾏。
下游应⽤和可靠消息服务之间的交互图如下:
1. 下游应⽤监听 MQ 消息组件并获取消息
2. 下游应⽤根据 MQ 消息体信息处理本地业务
3. 下游应⽤向 MQ 组件⾃动发送 ACK 确认消息被消费
4. 下游应⽤通知可靠消息系统消息被成功消费,可靠消息将该消息状态更改为已完成。
以上每⼀步都可能出现失败情况,分析⼀下这 4 步出现异常后下游业务和消息状态是否⼀致:
通过分析以上两个阶段可能失败的情况,为了确保上下游数据的最终⼀致性,在可靠消息系统中,需要开发消息状态确认和消息重发两个功能以实现 BASE 理论的 Eventually Consistent特性。
3.3、消息状态确认
可靠消息服务定时监听消息的状态,如果存在状态为待确认并且超时的消息,则表⽰上游应⽤和可靠消息交互中的步骤 4 或者 5 出现异常。
可靠消息则携带消息体内的信息向上游应⽤发起请求查询该业务是否已执⾏。上游应⽤提供⼀个可查询接⼝供可靠消息追溯业务执⾏状态,如果业务执⾏成功则更改消息状态为已发送,否则删除此消息确保数据⼀致。具体流程如下:
1. 可靠消息查询超时的待确认状态的消息
2. 向上游应⽤查询业务执⾏的情况
3. 业务未执⾏,则删除该消息,保证业务和可靠消息服务的⼀致性。业务已执⾏,则修改消息状态为已发送,并发送消息到 MQ 组件。
3.4、消息重发
消息已发送则表⽰上游应⽤已经执⾏,接下来则确保下游应⽤也能正常执⾏。
可靠消息服务发现可靠消息服务中存在消息状态为已发送并且超时的消息,则表⽰可靠消息服务和下游应⽤中存在异常的步骤,⽆论哪个步骤出现异常,可靠消息服务都将此消息重新投递到 MQ 组件中供下游应⽤监听。
下游应⽤监听到此消息后,在保证幂等性的情况下重新执⾏业务并通知可靠消息服务此消息已经成功消费,最终确保上游应⽤、下游应⽤的数据最终⼀致性。具体流程如下:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。