
在支付系统的架构设计中,高可用性始终是核心追求之一。当系统面临极端场景下的数据一致性挑战时,最终一致性成为无法回避的现实选择。这种看似矛盾的关系,实际上揭示了分布式系统固有的权衡——在CAP定理的约束下,可用性与一致性往往需要动态平衡。支付场景的复杂性在于,它不仅要求系统能够持续对外服务,还必须确保资金流转的准确性、可追溯性与合规性。因此,数据最终一致性并非退而求其次的妥协,而是经过精密设计后,在特定时空条件下实现高可用的必然路径。
首先需要明确,支付高可用架构中的“终极挑战”并非技术本身,而是对业务语义的深刻理解与映射。传统关系型数据库通过ACID事务保证强一致性,但在分布式环境下,网络分区、节点故障、请求超时等问题频发,迫使架构师转向BASE理论——基本可用、软状态、最终一致性。这意味着,在设计初期就必须接受:在某些瞬间,不同节点的数据视图可能不一致。但支付系统的特殊性要求这种不一致必须在可控范围内,且最终能收敛到正确状态。例如,用户发起转账后,扣款成功但入账延迟,系统必须确保这笔延迟不会导致错账、重复支付或资金丢失。
实践路径的第一步是定义最终一致性的粒度。在支付系统中,核心操作包括账户余额变更、交易流水记录、订单状态流转。这些操作可以解耦为不同的服务,各自拥有独立的数据库,通过事件驱动的方式进行异步同步。关键在于,每个服务内部必须实现本地事务的强一致性,而跨服务的状态同步则采用最终一致性模型。这种边界的划分直接影响系统的复杂度:边界越细,一致性保障难度越大;边界越粗,可用性可能降低。以支付网关为例,其核心是处理外部渠道返回的支付结果,此时网关服务需将结果持久化后,通过消息队列通知后续服务。如果消息投递失败,系统必须通过重试、补偿、对账等机制确保最终一致。
补偿机制是最终一致性的核心保障。在支付场景中,补偿并非简单的“回滚”,而是基于业务状态的异常处理。常见的模式包括“TCC”(Try-Confirm-Cancel)与“Saga”。TCC要求每个服务预留资源,在确认阶段完成实际操作,取消阶段释放资源。但TCC的缺陷在于,Try阶段如果失败,可能产生悬挂资源。而Saga通过编排一系列本地事务,当某个步骤失败时,依次执行逆向操作。在实际应用中,Saga更适用于支付流程,因为它不要求资源预留,但需要精确的补偿逻辑。例如,在订单支付流程中,扣减库存、生成订单、扣减余额、增加积分等步骤,如果余额扣减失败,补偿逻辑不仅需要恢复库存,还需考虑订单状态的回滚,同时避免重复执行。
幂等性是最终一致性的另一基石。高可用架构中,请求可能会被重复发送,尤其在网络超时或服务降级时。如果系统无法识别重复请求,可能导致资金损失。解决思路包括:在关键操作上使用全局唯一ID去重,或利用数据库的唯一约束。例如,支付确认接口必须保证,同一笔支付重试多次只能成功一次。幂等设计需要扩展到补偿场景——补偿操作自身也需幂等,以确保多次补偿不会产生副作用。
分布式事务的最终一致性实践还依赖精确的超时与重试策略。支付请求通常有明确的超时阈值,超时后系统应主动转换为异步查询,并最终根据渠道返回结果更新状态。这里的关键是,超时后不能直接判定失败,因为可能请求已成功只是响应丢失。因此,系统需要设计“查询-重试-补偿”的循环,直到状态收敛。同时,重试间隔需遵循指数退避原则,避免雪崩效应。更高阶的方案是引入“一致性时钟”或“混合逻辑时钟”,以精确判断事件顺序,但这类方案对基础设施要求较高,支付系统通常更青睐简单可靠的时间戳+版本号机制。
监控与对账是最终一致性的“最后防线”。即使架构设计再精妙,也无法完全避免系统异常或代码缺陷。因此,支付系统必须建立实时监控与离线对账体系。实时监控关注异常率、状态不一致比例、消息队列堆积等指标;离线对账则每日或每小时对比各方数据,识别差异并触发自动修复。这种“先验证后修正”的模式,实际上将一致性保障从运行时延伸至生命周期管理。例如,当对账发现某笔交易在A系统成功而在B系统未更新时,补账或补发消息的策略需预先定义。对账本身必须考虑最终一致性的收敛时间——如果设定1分钟内必须一致,则系统需支持快速补偿;如果允许数小时,则可采用慢速但更可靠的同步机制。
从更抽象的视角看,支付高可用架构的“终极挑战”在于如何在业务可接受的时空范围内,将最终一致性转化为一种可控、可审计、可追溯的确定性过程。这要求架构师放弃对“零不一致”的执念,转向对“不一致的度量与纠错”的严谨管理。以用户感知为例,短期的不一致(如余额显示延迟)不会造成信任危机,但长期的不一致(如交易状态错误)则不可容忍。因此,最终一致性的实现路径本质上是业务容忍度、技术复杂度与运营成本的优化组合。
现实中,支付系统往往混合使用多种策略。例如,核心账户变更采用同步强一致性(通过分布式锁与共识算法),而辅助性业务(如积分、通知)采用异步最终一致性。这种“分层一致性”模型能兼顾性能与可靠性。同时,随着技术演进,新的工具如云原生事件总线、可观测性平台、自动化故障恢复系统,正在降低最终一致性的实现门槛。但无论工具如何变化,核心原则不变:设计应始终从业务语义出发,通过明确的边界、幂等接口、补偿机制、监控对账,将最终一致性从“妥协”升华为“价值”——即系统在极端情况下仍能对外提供服务,并最终达到正确的状态。
支付高可用架构中的最终一致性,不是简单的“最终能成功”的乐观承诺,而是通过严密的技术手段、清晰的业务规则与完善的运营流程,构建出一个在不确定性中确保确定性的系统。这种能力,恰恰是支付系统从可用迈向“可信可用”的关键,也是分布式技术在实践中能真正落地的标志。
微服务的优缺点是什么?说下你在项目中碰到的坑。
微服务的优缺点及项目实践中的挑战一、微服务的核心优点
二、微服务的主要缺点
三、项目实践中的典型问题与解决方案
四、总结
微服务架构通过解耦和独立扩展提升了开发效率与系统韧性,但需权衡其带来的复杂性。
适合场景:业务复杂度高、团队规模大、需快速迭代的中大型系统。
慎用场景:业务简单、团队资源有限或对性能要求极高的场景。
实际项目中需结合自动化工具、完善监控体系和合理的拆分策略,才能最大化发挥微服务优势。
构建高可用的保险新核心系统
构建高可用的保险新核心系统需从数据架构转型和应用架构转型两方面入手,结合业务特性与技术创新,实现系统在突发流量、7×24小时运营及快速服务响应等场景下的高可用性。以下是具体方案:
一、数据架构转型:打破数据库依赖,提升扩展性与稳定性
传统保险核心系统依赖单一数据库处理所有业务,导致扩展周期长、并发能力受限。新架构需通过数据分层与解耦实现灵活扩展:
图1:大数据库和单体应用拆分示意图
二、应用架构转型:服务化拆分与异步处理,提升系统弹性
基于新数据架构,应用需从单体向分布式服务化转型,通过解耦与异步化提升并发处理能力:
图2:完整的互联网应用架构示意图
三、实施效果与持续演进
通过上述转型,阳光保险新一代核心系统已实现:
核心系统架构需随业务需求与技术发展持续演进,例如引入AIops实现智能运维,或通过Serverless架构进一步降低资源成本。
保证分布式系统数据一致性的6种方案
编者按 :本文由「高可用架构后花园」群讨论整理而成。
有人的地方,就有江湖
有江湖的地方,就有纷争
在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性?
具体业务场景如下,比如一个业务操作,如果同时调用服务 A、B、C,需要满足要么同时成功;要么同时失败。
A、B、C 可能是多个不同部门开发、部署在不同服务器上的远程服务。
在分布式系统来说,如果不想牺牲一致性,CAP 理论告诉我们只能放弃可用性,这显然不能接受。
为了便于讨论问题,先简单介绍下数据一致性的基础理论。

强一致
弱一致性
最终一致性
在工程实践上,为了保障系统的可用性,互联网系统大多将强一致性需求转换成最终一致性的需求,并通过系统执行幂等性的保证,保证数据的最终一致性。
但在电商等场景中,对于数据一致性的解决方法和常见的互联网系统(如 MySQL 主从同步)又有一定区别,群友的讨论分成以下 6 种解决方案。
业务整合方案主要采用将接口整合到本地执行的方法。
拿问题场景来说,则可以将服务 A、B、C 整合为一个服务 D 给业务,这个服务 D 再通过转换为本地事务的方式,比如服务 D 包含本地服务和服务 E,而服务 E 是本地服务 A ~ C 的整合。
优点: 解决(规避)了分布式事务。
缺点: 显而易见,把本来规划拆分好的业务,又耦合到了一起,业务职责不清晰,不利于维护。
由于这个方法存在明显缺点,通常不建议使用。
此方案的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。
消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。
人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。
消息日志方案的核心是保证服务接口的幂等性。
考虑到网络通讯失败、数据丢包等原因,如果接口不能保证幂等性,数据的唯一性将很难保证。
eBay 方式的主要思路如下。
Base:一种 Acid 的替代方案
此方案是 eBay 的架构师 Dan Pritchett 在 2008 年发表给 ACM 的文章,是一篇解释 BASE 原则,或者说最终一致性的经典文章。
文中讨论了 BASE 与 ACID 原则在保证数据一致性的基本差异。
如果 ACID 为分区的数据库提供一致性的选择,那么如何实现可用性呢?答案是
BASE (basically available, soft state, eventually consistent)
BASE 的可用性是通过 支持局部故障 而不是系统全局故障来实现的。
下面是一个简单的例子:如果将用户分区在 5 个数据库服务器上,BASE 设计鼓励类似的处理方式,一个用户数据库的故障只影响这台特定主机那 20% 的用户。
这里不涉及任何魔法,不过它确实可以带来更高的可感知的系统可用性。
文章中描述了一个最常见的场景,如果产生了一笔交易,需要在交易表增加记录,同时还要修改用户表的金额。
这两个表属于不同的远程服务,所以就涉及到分布式事务一致性的问题。
文中提出了一个经典的解决方法,将主要修改操作以及更新用户表的消息 放在一个本地事务 来完成。
同时为了避免重复消费用户表消息带来的问题,达到多次重试的幂等性, 增加一个更新记录表 updates_applied来记录已经处理过的消息。
系统的执行伪代码如下
(点击可全屏缩放图片)
基于以上方法,在第一阶段,通过本地的数据库的事务保障,增加了 transaction 表及消息队列 。
在第二阶段,分别读出消息队列(但不删除),通过判断更新记录表 updates_applied 来检测相关记录是否被执行,未被执行的记录会修改 user 表,然后增加一条操作记录到 updates_applied,事务执行成功之后再删除队列。
通过以上方法,达到了分布式系统的最终一致性。
进一步了解 eBay 的方案可以参考文末链接。
随着业务规模不断地扩大,电商网站一般都要面临拆分之路。
就是将原来一个单体应用拆分成多个不同职责的子系统。
比如以前可能将面向用户、客户和运营的功能都放在一个系统里,现在拆分为订单中心、代理商管理、运营系统、报价中心、库存管理等多个子系统。
拆分首先要面临的是什么呢?
最开始的单体应用所有功能都在一起,存储也在一起。
比如运营要取消某个订单,那直接去更新订单表状态,然后更新库存表就 ok 了。
因为是单体应用,库在一起,这些都可以在一个事务里,由关系数据库来保证一致性。
但拆分之后就不同了,不同的子系统都有自己的存储。
比如订单中心就只管理自己的订单库,而库存管理也有自己的库。
那么运营系统取消订单的时候就是通过接口调用等方式来调用订单中心和库存管理的服务了,而不是直接去操作库。
这就涉及一个『 分布式事务 』的问题。
分布式事务有两种解决方式
1. 优先使用异步消息。
上文已经说过,使用异步消息 Consumer 端需要实现幂等。
幂等有两种方式, 一种方式是业务逻辑保证幂等 。
比如接到支付成功的消息订单状态变成支付完成,如果当前状态是支付完成,则再收到一个支付成功的消息则说明消息重复了,直接作为消息成功处理。
另外一种方式如果业务逻辑无法保证幂等,则要增加一个去重表或者类似的实现 。
对于 producer 端在业务数据库的同实例上放一个消息库,发消息和业务操作在同一个本地事务里。
发消息的时候消息并不立即发出,而是向消息库插入一条消息记录,然后在事务提交的时候再异步将消息发出,发送消息如果成功则将消息库里的消息删除,如果遇到消息队列服务异常或网络问题,消息没有成功发出那么消息就留在这里了,会有另外一个服务不断地将这些消息扫出重新发送。
2. 有的业务不适合异步消息的方式,事务的各个参与方都需要同步的得到结果。
这种情况的实现方式其实和上面类似,每个参与方的本地业务库的同实例上面放一个事务记录库。
比如 A 同步调用 B,C。
A 本地事务成功的时候更新本地事务记录状态,B 和 C 同样。
如果有一次 A 调用 B 失败了,这个失败可能是 B 真的失败了,也可能是调用超时,实际 B 成功。
则由一个中心服务对比三方的事务记录表,做一个最终决定。
假设现在三方的事务记录是 A 成功,B 失败,C 成功。
那么最终决定有两种方式,根据具体场景:
对 b 场景做一个特殊说明:比如 B 是扣库存服务,在第一次调用的时候因为某种原因失败了,但是重试的时候库存已经变为 0,无法重试成功,这个时候只有回滚 A 和 C 了。
那么可能有人觉得在业务库的同实例里放消息库或事务记录库,会对业务侵入,业务还要关心这个库,是否一个合理的设计?
实际上可以依靠运维的手段来简化开发的侵入,我们的方法是让 DBA 在公司所有 MySQL 实例上预初始化这个库,通过框架层(消息的客户端或事务 RPC 框架)透明的在背后操作这个库,业务开发人员只需要关心自己的业务逻辑,不需要直接访问这个库。
总结起来,其实两种方式的根本原理是类似的,也就是 将分布式事务转换为多个本地事务,然后依靠重试等方式达到最终一致性 。
交易创建的一般性流程
我们把交易创建流程抽象出一系列可扩展的功能点,每个功能点都可以有多个实现(具体的实现之间有组合/互斥关系)。
把各个功能点按照一定流程串起来,就完成了交易创建的过程。
面临的问题
每个功能点的实现都可能会依赖外部服务。那么如何保证各个服务之间的数据是一致的呢?比如锁定优惠券服务调用超时了,不能确定到底有没有锁券成功,该如何处理?再比如锁券成功了,但是扣减库存失败了,该如何处理?
方案选型
服务依赖过多,会带来管理复杂性增加和稳定性风险增大的问题。
试想如果我们强依赖 10 个服务,9 个都执行成功了,最后一个执行失败了,那么是不是前面 9 个都要回滚掉?这个成本还是非常高的。
所以在拆分大的流程为多个小的本地事务的前提下,对于非实时、非强一致性的关联业务写入,在本地事务执行成功后,我们选择发消息通知、关联事务异步化执行的方案。
消息通知往往不能保证 100% 成功;且消息通知后,接收方业务是否能执行成功还是未知数。
前者问题可以通过重试解决;后者可以选用事务消息来保证。
所以目前只剩下需要实时同步做、有强一致性要求的业务场景了。
在交易创建过程中,锁券和扣减库存是这样的两个典型场景。
要保证多个系统间数据一致,乍一看,必须要引入分布式事务框架才能解决。
但引入非常重的类似二阶段提交分布式事务框架会带来复杂性的急剧上升;在电商领域,绝对的强一致是过于理想化的,我们可以选择准实时的最终一致性。
我们在交易创建流程中, 首先创建一个不可见订单 ,然后在同步调用锁券和扣减库存时,针对调用异常(失败或者超时),发出废单消息到MQ。
如果消息发送失败,本地会做时间阶梯式的异步重试;优惠券系统和库存系统收到消息后,会进行判断是否需要做业务回滚,这样就准实时地保证了多个本地事务的最终一致性。
业界常用的还有支付宝的一种 xts 方案,由支付宝在 2PC 的基础上改进而来。
主要思路如下,大部分信息引用自官方网站。
分布式事务服务简介
分布式事务服务 (Distributed Transaction Service, DTS) 是一个分布式事务框架,用来保障在大规模分布式环境下事务的最终一致性。
DTS 从架构上分为 xts-client 和 xts-server 两部分,前者是一个嵌入客户端应用的 JAR 包,主要负责事务数据的写入和处理;后者是一个独立的系统,主要负责异常事务的恢复。
核心特性
传统关系型数据库的事务模型必须遵守 ACID 原则。
在单数据库模式下,ACID 模型能有效保障数据的完整性,但是在大规模分布式环境下,一个业务往往会跨越多个数据库,如何保证这多个数据库之间的数据一致性,需要其他行之有效的策略。
在 JavaEE 规范中使用 2PC (2 Phase Commit, 两阶段提交) 来处理跨 DB 环境下的事务问题,但是 2PC 是反可伸缩模式,也就是说,在事务处理过程中,参与者需要一直持有资源直到整个分布式事务结束。
这样,当业务规模达到千万级以上时,2PC 的局限性就越来越明显,系统可伸缩性会变得很差。
基于此,我们采用 BASE 的思想实现了一套类似 2PC 的分布式事务方案,这就是 DTS。
DTS在充分保障分布式环境下高可用性、高可靠性的同时兼顾数据一致性的要求,其最大的特点是保证数据最终一致 (Eventually consistent)。
简单的说,DTS 框架有如下特性:
以下是分布式事务框架的流程图
实现
与 2PC 协议比较
1. 电商业务
公司的支付部门,通过接入其它第三方支付系统来提供支付服务给业务部门,支付服务是一个基于 Dubbo 的 RPC 服务。
对于业务部门来说,电商部门的订单支付,需要调用
从业务规则上需要同时保证业务数据的实时性和一致性,也就是支付成功必须加积分。
我们采用的方式是同步调用,首先处理本地事务业务。
考虑到积分业务比较单一且业务影响低于支付,由积分平台提供增加与回撤接口。
具体的流程是先调用积分平台增加用户积分,再调用支付平台进行支付处理,如果处理失败,catch 方法调用积分平台的回撤方法,将本次处理的积分订单回撤。
(点击图片可以全屏缩放)
2. 用户信息变更
分布式服务对衍生的配套系统要求比较多,特别是我们基于消息、日志的最终一致性方案,需要考虑消息的积压、消费情况、监控、报警等。
In partitioned databases, trading some consistency for availability can lead to dramatic improvements in scalability.
英文版 :
中文版:
感谢李玉福、余昭辉、蘑菇街七公提供方案,其他多位群成员对本文内容亦有贡献。

















暂无评论内容