分布式数据库 GaiaDB-X 金融应用实践
1 银行新一代核心系统建设背景及架构
在银行的 IT 建设历程中,尤其是中大行,大多都基于大型机和小型机来构建核心系统。随着银行业务的快速发展,这样的系统对业务的支持越来越举步维艰,主要体现在以下四个方面:
首先是难以支持银行快速发展的业务。随着国内电商、互联网支付、手机支付的迅猛发展,银行的交易量出现指数级的增长。比如我们的某银行客户,当前每秒的交易量峰值在一万多左右,预计在未来几年会逐渐增长到每秒 6 万笔交易,而且后续还会继续增长。在这种情况下,基于大型机的集中式架构,单纯靠硬件的升配,已经无法支持业务的持续增长。 第二是难以匹配银行系统的迭代更新速度。原有的基于大型机的胖核心架构,迭代周期往往在数月甚至半年。但随着银行间、以及银行与互联网金融企业之间的竞争加剧,银行迫切需要快速推出新业务进行创新,他们也希望能够像互联网公司那样,能够按周级进行快速迭代,快速响应业务需求。 第三是系统风险。银行业迫切需要做到软件及硬件的自主可控。 第四是生态封闭。大型机技术发展慢、人才难招。现在再去外面招一个懂 IBM 大型机的人已经很难了。
因为银行有和大型互联网公司相近的业务体量,因此在技术体系上,也借鉴了互联网公司的技术体系。
从 IaaS 层来看,银行采用了 X86、ARM 架构的通用服务器,也用到了混合云技术,大量使用了虚拟机与容器服务。
在 PaaS 层,银行使用了大量的分布式系统,如开源的微服务框架(例如 SpringCloud ),以及开源的或者商业的数据库,包括分布式/单机/关系型/缓存型的数据库,以及日志数据库 ES、时序数据库等。在中间件层,也用到了大量的开源的或者基于开源改造后的组件,例如消息队列、对象存储、分布式锁等。
在 SaaS 层,银行主要通过单元化 + 微服务的架构来实现分布式扩展。银行将自身业务应用划分成三种单元。
最上层是全局单元,主要是起到全局路由及流量分发的作用。 第二层是业务单元,核心的业务逻辑都在该部分来实现。同时,为了实现业务的横向扩展并支持数亿客户量,银行业跟互联网公司一样,对业务进行单元化拆分。例如我们接触到的某银行,就是将自身的业务拆分为了 16 个业务单元,每个单元五千万客户, 16 个单元部署在两个机房形成同城双活的架构。 最底层是公共单元,一些不太好或没必要做单元化拆分的应用,放在公共单元提供公共服务。
第一是分布式扩展性。由于采用了通用服务器,它的单机性能要远弱于大型机或者小型机。在这种情况下,数据库需要具备分布式可扩展的能力来满足上亿客户的金融需求。 第二是强一致性。金融场景对数据正确性、一致性的要求极高。因此要严格保障对事务的 ACID 特性。否则,业务层就要做大量的工作。 第三是容灾能力。通用服务器在硬件故障率方面要比大型机、小型机高很多。因此需要我们的数据库有更为可靠的可用机制以保障 SLA。同时,监管对于容灾能力的要求也在不断提升。比如,对于新建设的核心系统,监管要求必须满足 5 级以上的容灾能力,且满足同城双活并保证 RPO 为 0。在具体执行上,监管的要求也越来越严格,比如同城双活,之前是只需要具备相关的技术方案即可,但现在每年人行的监管都会直接到现场,要求做机房级实战故障切换。 第四是运维能力。系统下移到通用服务器并实现去 IOE,数据库节点数量要出现 50 倍的增长。以我们的一个银行客户举例,从小型机下移后,数据库节点数从 20 增长到 1000(当然这里面也预留了几倍的性能增量)。在和客户的交流过程中,他们也认同这个观点,认为系统下移后,节点数要增长一到两个数量级。但运维的人力不可能因此增加几十倍,在这种情况下,就要求我们的运维效率要有质的提升,需要能够智能化、自动化去做相关的运维工作。
2 分布式数据库 GaiaDB-X 的金融场景方案
计算层是无状态、可横向扩展的一层。它对外兼容 MySQL 协议,接收到 SQL 请求后,再经过 SQL 解析、权限检查、逻辑优化、物理优化之后,生成 DistSQL 下发到各个存储层的分片。为了达到更好的性能,逻辑与物理上尽量将数据过滤及计算能力下推到存储层,收到结果后,再把数据进行聚合计算,最后返回给客户端。 计算层的下面是存储层。它采用多分片的方式进行扩展,数据按照一定的分片规则打散到了各个分片中。我们支持 Hash、Range、List 等分区方式来做数据分区。同时,分片内数据采用多副本的方式来保证可靠性。 第三是 GMS 节点,即全局元数据管理模块。它用来管理全局性数据,例如表的 Schema 信息、权限信息、表的路由信息,还有后面要介绍到的用于做分布式事务的全局逻辑序列号。GMS 也采用多副本的方式,采用 Raft 协议进行数据同步。
第一阶段是从 2005 年开始,为了满足搜索、社区业务中大量读请求的场景,我们通过一主多从的集群化外加读写分离来扩展读性能。 第二阶段是为了支持凤巢广告系统与百度网盘,满足它们对万亿级数据量的需求,我们开始做分布式系统。到了 2014 年,我们就已经在凤巢广告系统中替换了基于 Oracle 的盘柜,为凤巢每年节省了上千万的成本。针对百度网盘,我们有个 3000 台服务器的集群支持网盘业务,所有网盘文件的元数据信息都存储在这里,最大的表达到万亿级记录数,至今仍是国内最大的关系型数据库集群之一。 第三阶段是随着百度钱包等泛互联网业务的兴起,对数据的一致性要求更高了。因此,我们实现了分布式事务强一致的特性,保障金融数据的正确性。 第四阶段,也就是现在。随着百度智能云对外技术输出,我们已经把数据库输出到了十余个行业,覆盖 150 家客户。在金融行业,GaiaDB-X 已经承接了金融核心业务,包括百信银行、银联商务、某交易所及国有行等。
扩展性的另外一个目的是自动做数据分离。在金融场景里面,存在多个业务共用一个数据库集群的场景,比如业务分为联机交易系统与查询类系统,数据库便对应划分为交易库和历史库两个。
对于交易库来说,只保存联机交易会频繁使用到的数据。例如账务结果数据及当天的交易记录,以满足对交易业务的快速响应。对于查询不那么频繁的即时交易记录,这可能就是一个相当大的数据量,一般都能达到千亿甚至万亿级别。这时,我们就可以将这个数据自动转移到历史库上去,用更高密度的存储机型来存储。一方面可以降低硬件成本,同时也可以避免对联机业务的影响。
这样做对业务来说,对外呈现的是一套数据库,业务可以根据需要来处理不同数据分区的逻辑,也不用在多套库之间通过 DTS 做数据同步。同时还把交易数据和历史数据做分离,以保障对联机业务的性能,同时也满足了查询业务的查询需求,避免其相互影响。
在金融场景中,对事物的 ACID 特性有着严格的要求:
持久性。指的是事务一旦提交就不会丢失,一般通过多副本 + 强同步来解决。 原子性。一个事务要么全部提交,要么全部回滚,不存在部分提交的情况。通常,数据库的 XA 协议,通过两阶段提交能解决这个问题。
但是 XA 协议不能很好地满足一致性与隔离性。以简单的转账场景为例,A、B 原来各有 100 块钱,总共 200 块。然后 A 给 B 转账 50 块钱。此时,我们会先给 A 扣 50,再给 B 加 50。如果通过 XA 协议来做的话,先走 prepare 再 commit,我们可以看到,commit(图中第 7、第 8 步)过程不是原子过程,存在一个执行时间差。在这个时间差内,另外一个事务去读取数据,就可能存在读到提交了一半的数据,A 和 B 的总和是 150 而不是 200。这是 XA + 2PC 解决不了的问题。
为了解决这个问题,业内一般是引入一个全局时钟来做一致性的保证。通常有三种实现方式:
TrueTime 方案。这个是大家比较熟悉的方案,Google Spanner 也发过论文。但它的缺陷是依赖硬件,要引入 GPS 与原子钟,这个一般来说是难具备的。 HLC 方案。采用该方案的典型数据库系统是 CockroachDB,它的优点是不依赖硬件,而且是去中心化的。但是缺点也很明显,一致性比较弱,而且要求服务器间的时钟误差不能超过 250ms,否则就无法正常运行。 TSO 方案,比如 TiDB 就是采用了这种方案。TSO 本质上来说是一个全局唯一而且自增的逻辑序列号,一个事务在开始时与事务 commit 时需要两次找 GMS 获取序列号,然后把 TSO 号作为版本号提交到存储层,存储层的 MVCC 机制来判断数据是否可见。它不需要硬件具备强一致性,但缺点是依赖一个全局中心的时钟分配器 GMS。但这并不是一个问题,因为刚刚我们也提到了,虽然 GMS 不具备扩展性,1200 万的 TPS 已经完全满足业务的常规需要了。因此我们最终采用了这种方案。
除了保障事务的一致性外,我们还需要保障上下游系统的数据一致性。在开始之前,我们首先要讲一下银行的典型业务场景,它一般分为三个子系统:
第一个是联机交易系统,例如存取款、在线支付等。这个系统的并发量高、延迟敏感,直接影响到用户体验。 第二个是跑批类的业务系统。例如结息,每天晚上半夜计算前一天的利息。这些业务是后台业务,有大量的读取与计算操作,延迟不敏感,但是对数据一致性要求高。怎么能够让这样的业务尽量避免对在线业务的影响,同时又能够读取到最新的数据呢?我们的方式是让跑批业务去读取从库数据,从而避免对主库的性能影响,同时结合 TSO,即全局逻辑时钟的对位对齐机制。等到从库水位追齐之后,才返回读取数据。当然这会引入一定的延时,但是因为跑批业务对响应延时不那么敏感,所以是可以接受的。 第三个子系统是大数据类的离线业务。通常来说,就是银行内部的各种大数据、数仓等系统,需要把数据库的数据同步过去。这样的业务对实时性要求不高,但要求最终的数据是一致的。由于各个计算节点都会承担流量,也会生成 BinLog。因此,如何对多份 BinLog 进行排序,让它们能够严格保持时序是我们需要解决的问题。我们的全局 BinLog 有两个模块,一个是 pump,从各个CN 节点抽取 BinLog,然后在 Sorter 节点基于 TSO(全局逻辑时钟)进行排序,来保障形成一个全局时序正确的 BinLog,以保障这个离线系统收到的数据的最终正确性。
根据人行的监管要求,银行核心系统一般要具备 5 级以上的容灾能力,这就要求具备两地三中心的容灾能力。
下图是我们客户的一个典型机房部署架构。首先在北京有两个同城机房,其物理距离在 50-100 公里左右,网路延迟在 1ms 左右。同时还有一个异地机房做灾备,物理距离一般是 1000 公里左右,比如合肥,网络延时是 10ms。
同城两个机房业务做双活部署,同时接受业务流量。数据库在两个机房采用 3 + 2 的部署形式,机房间采用强同步的方式来保障在发生机房级的故障之后,数据库能进行故障切换,而且保障数据的 RPO 为 0。
为保证单机房故障后的零数据丢失,我们采用分组强同步的方式,将 id1、id2 各划分一个复制组,复制组间采用强同步的方式。每个复制组保证至少有一个副本接收到数据之后才返回成功。这样在容忍少数副本故障的同时也能够保证单个机房故障后的零数据丢失。
异地机房的目标是当北京的两个机房都出现灾难性的事件之后,能够降级完成核心业务。它采用异步级联的方式来同步数据,首先异步是为了避免阻塞对主地域的数据库写入;采用级联方式,没有跟主地域机房形成复制组,主要一个为了保持灾备机房的数据库的拓扑独立性,减少依赖,保障在关键时刻可切换,另外也是降低跨地域同步的数据量,只需要同步一份数据即可。
结合在多家金融客户的实践,我们和中国信通院一起发布了金融数据库的容灾技术报告《金融级数据库容灾备份技术报告(2021 年)》。大家可以在公众号后台回复「金融级数据库容灾备份技术报告」获取。
3 金融应用案例介绍
下图是人行下面直属的某交易所。因为是涉及到国家金融稳定,所以在核心系统上需要逐步摆脱对 Oracle 的依赖,并拥有两地三中心的容灾能力。
由于当前的一些数据库都不能满足他们的业务场景需求,因此该交易所和百度采用了联合开发的模式,共建数据库能力。在两年的合作过程中,我们从外围的信息管理系统入手,逐步深入到核心交易系统,再到离线库数据分析系统,进而逐步实现数据库国产化。
此外,由于交易所的交易系统对低延时的要求较高,同时基于容灾要求又有同城双活的要求,如何在跨机房的情况下保障交易延时就成了亟待解决的问题。因此我们共同建设了 Collocate 机制,来尽量减少跨机房数据的访问,最终将交易延时从 80 毫秒降低到了 15 毫秒。
以上是今天分享的全部内容。
- - - - - - - - - - END - - - - - - - - - -
点击阅读原文,了解GaiaDB-X更多信息
推荐阅读