• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

分布式的最终一致性的方案

武飞扬头像
字节跳动技术团队
帮助20

知识储备

分布式系统中不可避免存在分布式事务带来的一致性问题。为了解决这个问题,需要熟悉业界相关的理论:

  • ACID
  • CAP
  • BASE
  • 2PC
  • 3PC
  • TCC

对于一致性的处理,分为强一致和最终一致性。强一致,对系统的吞吐量和性能有较大损耗,一般用在金融/银行系统,而最终一致性,是以牺牲短期的数据强一致、提升可用性的方案。 对于大部分分布式系统,强烈建议放弃强一致性,采取最终一致性方案。

跨系统调用存在的问题

同步调用

  • 现状:微服务之间采用HTTP调用,在一个事务内涉及跨系统调用,未考虑过事务一致性问题
  • 问题:在异常情况下一定出现数据不一致和脏数据

image.png

异步消息

  • 现状:采用消息队列进行模块解耦,相比第一方案,在吞吐量和可用性方面是更好选择。我们来分析下该方案
  • 问题:出现数据不一致
场景 本地事务 消息处理 出现原因 数据一致
1 本地处理成功 消息发送成功   一致
2 本地处理成功 消息发送失败 - 消息服务出问题  
  • 消息没有正确投递 | 没有不一致 | | 3 | 本地处理失败 | 消息发送失败 | | 没有不一致 | | 4 | 本地处理失败 | 消息发送成功 | - 发消息客户端超时,消息服务端成功
  • 发消息成功,然后A系统突然挂了 | 不一致 |

image.png

业界最终一致性方案

本地消息表

该方案的核心:

  1. 在发起远程调用前,先将远程调用的上下文持久化到一个消息表中,并要求消息表的操作与业务表的操作在一个本地事务中,然后通过异步机制去做远程调用。
  2. 消息表中维护了远程调用操作的状态机,当远程调用成功后,需要标记状态为成功。
  3. 有一点需要注意:如果遇到异步调用没有成功触发(网络原因或系统down机),需要有补偿重试机制,扫描本地消息表的数据,触发远程调用直到成功。

该方案实现方式较重,需要在每个使用该方案的业务系统专门维护一张消息表。

image.png

外部消息表

也称可靠型消息。和本地消息表的区别在于,将消息表移到了云端,由消息中间件统一管理消息的状态机,负责消息的初始化、重投、删除。RocketMQ是典型的例子。

image.png

image.png

Seata

Seata总共提供了4种模式,分别为AT、TCC、SAGA、XA。其中XA是强一致的,性能较差。

AT

AT是Seata主推的模式,是基于改进后的二阶段协议实现的。其技术核心是在每个服务的业务数据库中创建一个undolog表。

  1. 在事务第一阶段,Seata确保业务表与undolog表的操作在一个本地事务内。 在undolog表中,会分别记录事务提交前后的数据,称之为前镜像和后镜像,Seata框架会根据前后镜像以及当前SQL的类型,动态分析、计算出反向的回滚SQL。
  2. 在事务二阶段,如果需要提交,则会删除undolog;如果需要回滚,则Seata框架会执行底层自动生成的回滚SQL。

AT模式不能保证强一致,会存在中间状态,性能较高。AT要求我们拥有每个数据库的管理权,适用于企业内部的系统。

TCC

TCC是广为人知的模式,分为try、confirm、cancel三阶段。

try阶段就是对资源进行预占用,这个就需要对业务模型进行改造,增加中间态字段。

典型的例子,需要在单据表中增加维护预锁定资源的信息,例如锁定库存、预占用金额等。

confirm阶段和cancel阶段,将锁定资源释放,刷新实际资源信息,刷新库存、实际金额等。

TCC不是强一致的,同样存在中间状态的数据。它对业务系统的侵入性很高,所以使用场景比较局限。TCC和AT一样,要求我们拥有每个数据库的管理权。

SAGA

SAGA是基于状态机实现的二阶段协议。其原理:针对每个分支事务的正向业务逻辑,都要求提供一个反向的逻辑实现,以便在出现异常时可以调用反向逻辑进行回滚。SAGA的正向逻辑和反向逻辑,都需要程序员去实现,使用成本较高。它比较适用于长事务场景,尤其是涉及和第三方系统进行交互的场景(业务数据库无法由我方管理)。SAGA不是强一致的,同样存在中间态的数据。

事务消息接入

对数据一致性有要求的场景,可以使用rocketmq的事务型消息,接入比较简单。

使用方式

TransactionMQProducer,区别于发普通消息的DefaultMQProducer

@Override

public TransactionSendResult sendMessageInTransaction(final Message msg,

    final Object arg) throws MQClientException {

    if (null == this.transactionListener) {

        throw new MQClientException( "TransactionListener is null" , null);

    }



    return this.defaultMQProducerImpl.sendMessageInTransaction(msg, null, arg);

}

TransactionMQProducer初始化时要设置一TransactionListener。

事务提交和事务回查都在TransactionListener实现。

public interface TransactionListener {

    /**

 * When send transactional prepare(half) message succeed, this method will be invoked to execute local transaction.

 *

 *  @param msg Half(prepare) message

 *  @param arg Custom business parameter

 *  @return Transaction state

 */

    LocalTransactionState executeLocalTransaction(final Message msg, final Object arg);



    /**

 * When no response to prepare(half) message. broker will send check message to check the transaction status, and this

 * method will be invoked to get local transaction status.

 *

 *  @param msg Check message

 *  @return Transaction state

 */

 LocalTransactionState checkLocalTransaction(final MessageExt msg);

事务状态

public enum LocalTransactionState {      COMMIT_MESSAGE,      ROLLBACK_MESSAGE,      UNKNOW,  } 

executeLocalTransaction方法

有两种接入方法:

  • 标准的实现,是将本地的事务逻辑都写在此方法内部,但缺点是对代码的侵入性较大,尤其是当要对老代码进行改造时难度较大
  • 另外一种取巧的方法,本地事务逻辑正常写在其他处,然后在executeLocalTransaction方法中返回UNKNOW 状态,这样就完全依靠回查来决定事务的提交状态。

checkLocalTransaction方法

在此方法中汇报本地事务的提交、回滚状态。一般需要通过查询业务表来实现。 以电商系统为例,在订单生成时,发送消息通知物流系统生成物流单据,由于写入订单单据和发送消息要求保证原子性,而本地事务的状态,可以通过判断订单单据是否写入来判断,故checkLocalTransaction的逻辑是:根据订单号查询订单的记录

  • 如果订单记录不存在,表明事务未提交,需返回COMMIT_MESSAGE
  • 如果订单记录存在,表明事务提交,需返回ROLLBACK_MESSAGE
  • 如果方法执行异常,返回UNKNOW,等待rockermq下一次重试回调

总结

本文介绍了分布式系统下最终一致性的常用解决方案,包括本地消息表、事务消息、seata的几种事务模式,他们都有对应的场景。

  1. 本地消息表是一个可以满足多数业务要求的场景,可用性较高,如果不希望引入其他中间件,可以考虑该方案。在具体实践中,可以将消息的持久化、异步分发远程调用、补偿重试等共性逻辑封装成组件。
  2. 事务消息有比较广泛的使用场景,稳定性有保障,但由于依赖消息中间件,稳定性不如本地消息表,另外在出现问题时排查不大方便,建议对于链路监控多做考虑。
  3. seata的几种模式本文有详细介绍,在实践中要因地制宜的选择。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanfbegj
系列文章
更多 icon
同类精品
更多 icon
继续加载