专栏名称: 互联网后端架构
主要介绍Java后端架构。其中也会掺杂一些前端、GO、Python、Linux,目标:全栈工程师!---好像很牛叉的样子 ^-^
目录
相关文章推荐
美团技术团队  ·  空降香港!美团无人机率先在港启航 ·  2 天前  
美团技术团队  ·  美团大模型北斗实习计划2025——全球顶尖科 ... ·  2 天前  
架构师之路  ·  DeepSeek开源V3/R1架构设计思路, ... ·  4 天前  
架构师之路  ·  总有人问,出海怎么用DeepSeek满血版( ... ·  5 天前  
51好读  ›  专栏  ›  互联网后端架构

分布式环境下保证数据一致性的几种实现方式

互联网后端架构  · 公众号  · 架构  · 2020-03-05 18:28

正文

微服务下数据一致性的特点,总结了下目前的保障微服务下数据一致性的几种实现方式如下,以备后查。此篇文章旨在给大家一个基于微服务的数据一致性实现的大概介绍,并未深入展开,具体的实现方式本人也在继续学习中,如有错误,欢迎大家拍砖。

传统应用的事务管理


本地事务

在介绍微服务下的数据一致性之前,先简单地介绍一下事务的背景。传统单机应用使用一个RDBMS作为数据源。应用开启事务,进行CRUD,提交或回滚事务,统统发生在本地事务中,由资源管理器(RM)直接提供事务支持。数据的一致性在一个本地事务中得到保证。

分布式事务

两阶段提交(2PC)

当应用逐渐扩展,出现一个应用使用多个数据源的情况,这个时候本地事务已经无法满足数据一致性的要求。由于多个数据源的同时访问,事务需要跨多个数据源管理,分布式事务应运而生。其中最流行的就是两阶段提交(2PC),分布式事务由事务管理器(TM)统一管理。

两阶段提交分为准备阶段和提交阶段。

两阶段提交-commit

两阶段提交-rollback

然而两阶段提交也不能完全保证数据一致性问题,并且有同步阻塞的问题,所以其优化版本三阶段提交(3PC)被发明了出来。

三阶段提交(3PC)

三阶段提交

然而3PC也只能保证绝大多数情况下的数据一致性。

微服务下的事务管理


那么,分布式事务2PC或者3PC是否适合于微服务下的事务管理呢?答案是否定的,原因有三点:

  • 由于微服务间无法直接进行数据访问,微服务间互相调用通常通过RPC(Dubbo)或Http API(Spring Cloud)进行,所以已经无法使用TM统一管理微服务的RM。

  • 不同的微服务使用的数据源类型可能完全不同,如果微服务使用了NoSQL之类不支持事务的数据库,则事务根本无从谈起。

  • 即使微服务使用的数据源都支持事务,那么如果使用一个大事务将许多微服务的事务管理起来,这个大事务维持的时间,将比本地事务长几个数量级。如此长时间的事务及跨服务的事务,将为产生很多锁及数据不可用,严重影响系统性能。

由此可见,传统的分布式事务已经无法满足微服务架构下的事务管理需求。那么,既然无法满足传统的ACID事务,在微服务下的事务管理必然要遵循新的法则--BASE理论。

BASE理论由eBay的架构师Dan Pritchett提出,BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性,应用应该可以采用合适的方式达到最终一致性。BASE是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency)。

  1. 基本可用:指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。

  2. 软状态:允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。

  3. 最终一致性:最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

BASE中的最终一致性是对于微服务下的事务管理的根本要求,既基于微服务的事务管理无法达到强一致性,但必须保证最重一致性。那么,有哪些方法可以保证微服务下的事务管理的最终一致性呢,按照实现原理分主要有两类,事件通知型和补偿型,其中事件通知型又可分为可靠事件通知模式及最大努力通知模式,而补偿型又可分为TCC模式、和业务补偿模式两种。这四种模式都可以达到微服务下的数据最终一致性。

实现微服务下数据一致性的方式


可靠事件通知模式

同步事件

可靠事件通知模式的设计理念比较容易理解,即是主服务完成后将结果通过事件(常常是消息队列)传递给从服务,从服务在接受到消息后进行消费,完成业务,从而达到主服务与从服务间的消息一致性。首先能想到的也是最简单的就是同步事件通知,业务处理与消息发送同步执行,实现逻辑见下方代码及时序图。

public void trans() {
    try {
    // 1. 操作数据库
        bool result = dao.update(data);
    // 操作数据库失败,会抛出异常    
    // 2. 如果数据库操作成功则发送消息        
    if(result){            
        mq.send(data);
        // 如果方法执行失败,会抛出异常
     }    
    } catch (Exception e) {        
        roolback();
        // 如果发生异常,就回滚
    }
}

上面的逻辑看上去天衣无缝,如果数据库操作失败则直接退出,不发送消息;如果发送消息失败,则数据库回滚;如果数据库操作成功且消息发送成功,则业务成功,消息发送给下游消费。然后仔细思考后,同步消息通知其实有两点不足的地方。

  1. 在微服务的架构下,有可能出现网络IO问题或者服务器宕机的问题,如果这些问题出现在时序图的第7步,使得消息投递后无法正常通知主服务(网络问题),或无法继续提交事务(宕机),那么主服务将会认为消息投递失败,会滚主服务业务,然而实际上消息已经被从服务消费,那么就会造成主服务和从服务的数据不一致。具体场景可见下面两张时序图。

  2. 事件服务(在这里就是消息服务)与业务过于耦合,如果消息服务不可用,会导致业务不可用。应该将事件服务与业务解耦,独立出来异步执行,或者在业务执行后先尝试发送一次消息,如果消息发送失败,则降级为异步发送。

异步事件

本地事件服务:

为了解决上述同步事件中描述的同步事件的问题,异步事件通知模式被发展了出来,既业务服务和事件服务解耦,事件异步进行,由单独的事件服务保证事件的可靠投递。







请到「今天看啥」查看全文