支付系统如何进行分布式改造

简介: 分布式架构在容量、性能、稳定性、成本方面都具有巨大的优势。在高可用方面,核心思想之一是“解决一切单点”,单点容易出现故障,性能方面也可能成为瓶颈,因此需要将单点改造拆分成多个点。

原创声明:本文系作者原创,谢绝个人、媒体、公众号或网站未经授权转载,违者追究其法律责任。


传统支付系统面临的挑战

随着近年来移动支付的兴起 ,如条码支付、声波支付、NFC 近场支付等,随之还产生了聚合支付把多种支付方式聚合在一起,方便人们的使用,移动支付已经渗透到我们生活的每一个角落,不带钱包出门已经没有任何阻碍。这就给传统的支付系统提出了新的挑战,用户量激增,系统容量和性能跟不上了,传统的架构往往以 IOE 技术为主,采用 scale up 方式以更强的硬件提升系统性能和容量,扩容成本将是巨大的。支付机构是持牌机构都是受监管的,对系统稳定性有强要求,传统架构下往往都会用冷备的方式来进行容灾,意味着又要投入一倍的成本,由于数据库主备复制的延时,必须等到数据同步完成才可以切换,容灾切换时间长。进行分布式改造已经刻不容缓。

更多关于传统架构与分布式架构对比请参考《集中式架构与分布式架构比较》

分布式架构在容量、性能、稳定性、成本方面都具有巨大的优势。在高可用方面,核心思想之一是“解决一切单点”,单点容易出现故障,性能方面也可能成为瓶颈,因此需要将单点改造拆分成多个点。垂直拆分能更清晰化模块划分,区分治理,水平切分能解决大数据量性能瓶颈问题,分布式改造主要是将这两者结合起来,对传统架构进行全面的改造。

分布式改造之垂直拆分

垂直拆分就是将原来一个整体的系统按业务模块拆分成多个系统,系统内部数据是自包含的,不会与别的系统共用数据库,系统与系统之间的交互通过暴露和调用服务来实现。那么如何按照业务来拆分呢?

为了方便理解,首先我们来看一下一笔支付过程是如何进行的:

image.png

商户发起收单请求,经过 API 网关,调到产品层的“在线收单”产品

调用收银台选择支付方式,也可能直接进入支付环节,创建交易流水

进行支付处理,通过金融交换从银行扣客户帐,记录帐务流水,入商户帐,记录账务流水

对交易按照费率进行收费,记录收费的帐务流水。此时会异步触发营销和风控策略

日终会异步进行会计记帐(也有同步记会计帐的)、业会核对、清结算和对帐处理

从这个过程可以大概推演出支付系统的一般应用架构:
image.png

图:支付系统的应用架构

应用架构定义一个大型软件系统由哪些应用子系统构成,以及应用之间是如何分工和协作的。好的应用架构抽象合理、协作有序、易于扩展、能够复用。有了这个应用架构,我们就可以非常清晰的根据应用架构划分的子系统来进行垂直拆分。

从架构上来说,分为四层:

image.png

图:支付系统的分层

渠道层:商户和客户的交易请求的入口。一般会划分以下系统:商户网站、用户网站、无线接入、API 网关。

产品层:通过基础服务层提供的服务组装成具体业务场景功能,对客户、商户运营等人员提供服务。一般会把服务商户的功能划分为商户域,服务 C 端用户的划分为用户域。可以按照这两个域拆分成两个子系统,也可以更进一步根据不同产品特性再拆分,比如商户域中的收单产品、虚拟产品、垂直行业产品。

公共服务层:将各个产品都需要使用的些服务抽像成公共服务。一般会划分:收银台、交易支付、计费等系统。比如说产品层可以通过组装各种交易类型和收费规则形成不同的产品。

基础业务层:支付系统的核心,资金和客户信息的处理都在这里。一般会划分三大子系统:帐务核心、会计核心、会员核心。

其它支撑系统:

网关:负责与银行、银联等金融机构进行资金交换,与外部合作伙伴接入,如渠道拓展商、行业客户等。一般划分:银行接入网关和合作伙伴接入网关。

运营支撑:贯穿于四个层的是运营支撑域:一般会划分运营支撑、安全、风控、营销子系统。

垂直拆分本质上是服务化改造,除了上面讲的按业务拆分,还需要一套分布式服务框架的支撑。

分布式改造之水平拆分

前面讲的垂直拆分只是把系统按业务模块划分到不同的子系统,数据库也分到了不同系统,但没有解决单表大数据量的问题,而水平切分就是要把一个表按照某种规则把数据划分到不同表或数据库里。简单的说就是做分库分表。

在做分库分表之前我们需对数据模型进行分类,分为“流水型数据”、“状态型数据”和“配置型数据”。

流水型数据:像流水一样不断增长的数据,各条数据间是独立的。如支付订单、交易流水、帐务流水(入帐/出帐)、会计流水等。

状态型数据:代表一个对象当前的状态的数据。如会员信息、客户信息、帐户信息、会计帐。

为什么有会员信息还有客户信息?会员往往是注册在支付平台的用户,一个人可以注册多个会员,但是一个自然人只可能有一个客户信息,一个会员通过实名认证后就关联上了客户信息。无论一个客户注册多少个会员,实名认证后都只有一个客户信息。

配置型数据:系统中用作为配置的数据。如产品、手续费率、分支机构信息、支付路由规则、会计科目等。

流水型数据会不断产生,且各条数据间是独立的,天然适合进行分库分表。

状态型数据读写比相当,每一次写操作必须基于前一个正确的状态,可以评估一下数据量的大小,数据量如果大或者要实现单元化架构,也需要进行分库分表,提高并发处理能力,同时方便隔离故障影响。

配置型数据,读多写少,强依赖读,弱依赖写,不要求严格的读一致性,且配置型数据一般数据量不会很大,不需要进行分库分表设计。但是业务处理中往往又需要用到,传统架构的老系统可能使用了一些关联表操作,关联到了配置数据,分库后其它数据与配置不在一个库,不能进行关联表操作,由于配置型数据不要求严格的读一致性的特点,可以将配置型数据加载到分布式缓存里,由业务代码来做“join”。

那么分库分表按照什么规则来拆分呢?通常不会按实体 id 进行 hash 取模的方式来拆分。因为希望同一个用户的数据能够在同一个数据库中,尽量避免产生分布式事务。业界普遍的做法是通过用户维度来进行拆分。由于不同实体 id 的值不同,且不能保证每个实体和请求中都包含用户 id,所以简单的用实体 id 或用户 id 进行 hash 取模将不能保证同一个用户的数据都落在同一个分片。

一种推荐做法是,在用户创建的时候给该用户随机或一定规则(如地区)生成一个两位的分片号 00~99(两位意味着可以分成百库百表,通常够用了),那么在生成与该用户相关的所有实体的 id 的时候,都约定把这个分片号拼接到这个 id 中。在分布式数据访问框架中进行路由选择时,就可以取 id 中的分片号进行路由,而不依赖于用户 id。且在排查问题的时候也非常方便定位数据的存储位置。

下面是一个参考的 id 生成规则示例:

image.png

所以数据水平拆分除了需要一个强大的分库分表数据访问中间件,还需要一个分布式序列生成器。当然这个生成器也可以是集成在分库分表数据访问中间件中的一个功能。

那么如果一笔交易涉及多个用户按谁的 id 来拆分呢?比如一笔转账或支付,涉及转出方/转入方或支付方/收款商户。这种情况一般可以按资金转出方来拆分。

分布式改造后带来的问题如何应对

分布式事务产生

由于按用户维度进行了分库分表,可能存在跨数据库的事务,比如说,转账交易中转出方和转入方的账户不在同一个数据库中,这就产生了分布式事务。通常不会用 XA 协议来解决,因为 XA 协议锁资源性能太差,通常是通过 TCC 柔性事务来解决。具体可以参见进阶阅读《分布式事务综述》。

跨表查询如何解决

由于分库分表后,不能进行跨库的连表查询,原来的一些很常见的查询操作变得很麻烦。对于不是以用户为维度的汇总查询也非常麻烦。比如说支付交易流水是按发起方用户(支付方)进行拆分的,用户需要查询自己的账单很容易。但是商户要查询账单就比较麻烦了,要去所有的库里遍历、汇总、分页。也非常耗系统资源。所以一般会做一些数据冗余,例如专门实现一个账单系统,通过消息队列异步将用户的交易流水同步过来,T+1 跑批再按商户维度进行拆分,并生成商户账单。查询帐单都从帐单系统中查询。

还可以通过异构索引来查询和做 OLAP 分析,异构索引就是将数据同步到 ElasticSearch,利用 ES 的强大索引能力来做查询和分析,为了使业务更容易使用,可以利用数据访问代理层来屏蔽底层是路由到数据库还是路由到 ES。

如何进行数据同步

企业都有做大数据分析的需求,需要将数据同步大数据平台,如 Hadoop。分库分表之后,数据同步会比较复杂,毕竟之前是单表同步到 Hadoop 比较简单,但是 100 张表同步到 Hadoop 里会复杂一些。这时就需要设计一套专门的数据模型管理平台,数据模型、分库分表规则等由这个平台来管理,当需要使用数据的时候通过(应用/逻辑表)维度订阅数据即可,不用单独订阅物理表。不仅是数据同步,凡是有业务需要用到各种数据,都可以通过这个平台来订阅,帮助企业数据业务快速发展。

分库分表后批处理任务怎么处理

批处理任务,比如有日终对账、清算、生成账单等,原来在一个数据库中的时候,由一个应用 Server 去数据库中捞取流水就可以了。但是分库分表后流水都落在很多库里,一个 Server 去每个库里遍历显然不是一个很好的办法,且不能充分利用机器资源,提高批处理效率,甚至由于处理的数据量太大在日终低峰期内根本无法完成任务。

前面提到各条流水数据之间没有关联的,完全可以并发的进行处理,每个 Server 捞取一个分片的数据进行处理。那么就需要有一个很好的调度系统来协调,可以采用三层调度的方式。

image.png

图:三层调度示意图

第一层 split:把任务按照分片规则拆分成多个 Load 任务,并发送到集群中的 Server 去执行。

第二层 load:每个 load 任务捞取一个分片的数据,逐条创建 execute 任务,并发送到集群中的 Server 去执行。注意:捞取数据要进行流量控制以免数据量太大把集群打满。

第三层 execute:执行具体的一条数据的逻辑。

三层架构并不是说一定都需要三层,可以根据业务逻辑来定制只有两层也可以。

如何进行数据扩容

通常可以采用“预分配”的方式来做,即一开始就按一个比较长期的容量来规划分片数,比如百库百表。但实际上一开始并没有这么大的量,所以实际只有两个数据库 Server,在这两个 Server 上分别建 50 个 schema,逻辑上仍然是 100 个分库,物理上只有 2 个数据库 Server。当容量不够的时候,为了保证数据的均衡,通常会采用成倍扩容的方式,再加两台数据库 Server,然后分别迁移 25 个 schema 到这两个数据库 Server 上,数据也搬过来。由于数据同步有延时,全量数据同步完成后,两边的 schema 都禁写,待增量数据同步完成后打开新的 schema 写,会产生短暂的部分用户交易失败,重试一下即可,在低峰期做迁移,产生小范围失败一般是可以接受的。由于逻辑分片数没有变化,扩容成本比较低。通常不会用改变分片规则的方式来扩容,因为改变分片规则需要进行数据重新分布,成本和风险巨大。

如何进行容灾

同城容灾:通常可以同城多机房部署应用,数据库只有一个机房处于 Active 状态,所有机房的应用都连这个机房的数据库,另一个机房的数据库为备库,进行主备复制,当备机房发生灾难时业务不会中断,但业务会跌一半,当主机房发生灾难时,数据库切换备库,会有短暂的业务中断。

异地冷备:应用也是异地多机房部署,由于异地网络延时不可忽略,异地备机房是处于 standby 状态,正常是没有流量的,冷备机房采用数据库主备同步的方式同步数据,这种方式灾备切换时间长,成本投入高。

异地多活:应用采用异地多机房单元化部署架构,每个机房的应用都是可以提供服务的,单元内是自包含部署全量应用,每个单元服务多个分片的用户,单元化架构可以参考《素描单元化》。由于异地网络延时是不可忽略的,数据层的容灾方案也是分“流水型”、“状态型”、“配置型”数据采用不同的容灾策略。具体可参考《分布式系统数据层设计模式》。

如何更好的排查和分析问题

分布式改造后整个系统架构已经是服务化了,原来通常可以通过查本地日志来定位问题。但现在一个交易由若干个系统协同完成,我们需要一套分布式链路跟踪系统或 APM(应用性能管理)系统来协助我们看清整个系统的全貌,分析排查问题。那么如何进行分布式链路跟踪呢?可以通过 OpenTracing 标准对整个分布式架构中的中间件和应用进行埋点或自动植入探针实现。

总 结

分布式架构有着海量、成本、稳定、速度的优势,但它也不是银弹,分布式改造是一个较为复杂的工程,既需要熟悉业务,能够设计出整个系统的业务架构,按照业务架构来进行垂直拆分,又需要熟悉数据模型,区分“流水型”、“状态型”、“配置型”数据,根据不同类型数据的特点将它他按用户维度进行拆分,还需要熟悉分布式中间件的运用。分布式中间件在整个分布式架构中起着至关重要的作用,将技术构架与业务结合起来。蚂蚁金服通过多年金融级架构的演进,经过多年双十一大促的验证,已经形成了一套业界领先的金融级分布式架构,请参考《金融级分布式交易的技术路径》。

目录
相关文章
|
15天前
|
存储 运维 负载均衡
构建高可用性GraphRAG系统:分布式部署与容错机制
【10月更文挑战第28天】作为一名数据科学家和系统架构师,我在构建和维护大规模分布式系统方面有着丰富的经验。最近,我负责了一个基于GraphRAG(Graph Retrieval-Augmented Generation)模型的项目,该模型用于构建一个高可用性的问答系统。在这个过程中,我深刻体会到分布式部署和容错机制的重要性。本文将详细介绍如何在生产环境中构建一个高可用性的GraphRAG系统,包括分布式部署方案、负载均衡、故障检测与恢复机制等方面的内容。
68 4
构建高可用性GraphRAG系统:分布式部署与容错机制
|
1月前
|
消息中间件 中间件 数据库
NServiceBus:打造企业级服务总线的利器——深度解析这一面向消息中间件如何革新分布式应用开发与提升系统可靠性
【10月更文挑战第9天】NServiceBus 是一个面向消息的中间件,专为构建分布式应用程序设计,特别适用于企业级服务总线(ESB)。它通过消息队列实现服务间的解耦,提高系统的可扩展性和容错性。在 .NET 生态中,NServiceBus 提供了强大的功能,支持多种传输方式如 RabbitMQ 和 Azure Service Bus。通过异步消息传递模式,各组件可以独立运作,即使某部分出现故障也不会影响整体系统。 示例代码展示了如何使用 NServiceBus 发送和接收消息,简化了系统的设计和维护。
47 3
|
1月前
|
消息中间件 存储 监控
消息队列系统中的确认机制在分布式系统中如何实现
消息队列系统中的确认机制在分布式系统中如何实现
|
1月前
|
消息中间件 存储 监控
【10月更文挑战第2天】消息队列系统中的确认机制在分布式系统中如何实现
【10月更文挑战第2天】消息队列系统中的确认机制在分布式系统中如何实现
|
1月前
|
存储 开发框架 .NET
C#语言如何搭建分布式文件存储系统
C#语言如何搭建分布式文件存储系统
70 2
|
30天前
|
消息中间件 存储 监控
消息队列系统中的确认机制在分布式系统中如何实现?
消息队列系统中的确认机制在分布式系统中如何实现?
|
1月前
|
存储 分布式计算 监控
C# 创建一个分布式文件存储系统需要怎么设计??
C# 创建一个分布式文件存储系统需要怎么设计??
35 0
|
1月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
3月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
112 2
基于Redis的高可用分布式锁——RedLock
|
8天前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
41 16

热门文章

最新文章