它的实现技术一直在改变,不变的是其作为电子账本的本质。因IT技术的发展,银行IT系统逐渐变成一个系统群,其中银行核心系统主要负责管理账户,包括账户金额管理和账户状态管理。
银行核心系统演化进程示意图
银行核心系统的自主可控事关国家安全和社会稳定,该系统的分布式改造是实现其自主可控的途径之一,也是阿里巴巴提出的“去IOE”的实现方案之一,同时分布式改造也能降低银行核心系统复杂度,降低系统升级、维护的难度和风险。这些年国内几大银行和实力较强的银行系统厂商在做银行核心系统的分布式改造或改造准备,也有银行系统厂商与国内数据库企业合作,为城商行级别的银行实现了分布式系统落地投产。
目前银行核心分布式改造还处于技术验证或尝试阶段,尚未有大型银行落地投产分布式银行核心系统。作者参与过一家全国性的股份制银行分布式核心系统研发,也曾作为项目经理组织公司的分布式核心研发。下文就分布式银行核心研发相关内容进行详细论述。
一、分布式银行核心概述
集中式银行核心系统中一般分为存款、贷款、公共三大模块。其中存款一般分对公、对私、内部及客户模块。公共分现金、凭证、机构柜员。贷款模块在核心的功能较少,其主要功能是放在贷款系统里,但贷款模块业务逻辑较为复杂。
存款模块是负责账务处理的模块,是核心系统最重要最复杂的模块,在核心系统项目建设中主力人员都会投放在该模块,该模块的主要交易有“开销存取转”,也就是开户、销户、存款、取款、转账。这几个功能一般会按对公和对私拆分为两组交易,按定期与活期也会进行拆分,还有内部账一般会划为较独立的功能。
“开销存取转”主体功能按对公、对私和活期、定期、内部账演化出的子功能是整个核心系统中最重要的功能。其次是存款模块的账户状态控制功能,例如挂失、冻结。再次就是账户相关的辅助功能,例如日常交易撤销、更换介质、配介质、账户信息维护、账户合并等功能。
产品与利率、交易与手续费、客户/客户群组与利率及手续费、限额等贯穿整个系统的业务逻辑。这些业务逻辑再加上日终批处理,就形成了当前庞杂的银行核心系统。当前主流银行核心架构如图1-1所示:
图1-1 当前主流银行核心架构图
当前银行核心系统由于过于庞杂,升级维护难度大、风险高。这一点在维护较复杂且较老的核心系统时会深有体会,每上线一个功能都是心惊胆战的。
近几年随着银行核心从“胖核心”到“瘦核心”的演变,以及银行IT系统个数随着IT技术的发展而增加,银行IT系统形成了一个系统群,这个系统群一般是由ESB把各系统串联起来,ESB比较擅长接口适配功能,但不擅长接口管理及流程控制,这就导致各系统之间协同提供服务很困难,各系统之间形成了信息孤岛,各业务部门开展业务各自为营,各部门之间很难协同为客户提供深层次的服务,再加上资管新规出台。“业务中台”的概念逐渐被接受和传播。
业务中台是整合银行IT系统群的整合平台,它在IT系统层面上打破了信息孤岛。若在人员组织上也按中台的模式进行重组,可打通各部门之间原来的权责壁垒,业务中台就能组织各系统信息和资源为客户提供深度服务。但是,现有银行核心系统需要进行分布式改造才能为中台提供原子服务。
图1-2 业务中台简要架构
分布式银行核心架构与当前主流核心架构有一定区别,分布式银行核心在逻辑处理上分为两层,底层是原子服务层,上层是组合层。
原子服务层按业务分类一般分为存款子系统、介质子系统、产品子系统、现金子系统、凭证子系统、机构柜员子系统。组合层可以按业务条线划分子系统,也可以按支付和非支付划分。
按业务条线划分组合层会更加精细,有业务条理,避免组合层复杂度过高,难于开发运维。按业务条线划分组合层也利于从业务层面指导系统开发运维,使系统离业务人员更近,便于业务处理和业务创新。但是,按支付与非支付划分组合层划分抽象程度更高,也就意味着灵活度的更高,可以实现更加灵活多变的业务场景。
银行分布式核心需要一套完整的技术组件和技术平台支撑运行及开发运维。分布式应用架构需支持交易路由、交易熔断降级、数据路由、注册中心、安全中心、统一配置中心等功能。
服务治理平台应支持服务功能管理、应用节点管理、统一日志平台、版本及部署管理等功能。分布式开发工具应支持代码开发、版本管理、部署管理等功能。分布式监控平台应支持交易耗时监控、数据库监控、网络监控、CPU监控、IO监控等功能。
图1-3 分布式技术架构示意图
对银行分布式核心应用进行拆分解耦时,数据库也需要拆分。数据库按业务子系统拆分也叫垂直分库。垂直分库正常情况下直接按子系统拆分应用后,应用逻辑操作的表就是该模块的数据表,把这些表从原来的数据库里分离出来导入新库就完成了分库。
但是,有时候一个子系统的程序逻辑会直接操作另一个子系统的表,这需要对子系统进行解耦合,从业务角度分析一个表所属的子系统。
还有一种情况是一个表从业务角度分析存在两个模块的要素,这就需要把该表拆分成两个表,分别把相应的字段放到相应的子系统中。在垂直分库中比较难以把握的是一些关联关系表,例如机构与产品关系表、介质与账户关联表等,它们同时存在两个模块的要素并且需要有对应关系,这类表的划分主要取决于这类关系表放在哪个模块管理更合适。以机构与产品关系表为例,若机构与产品关系放到参数平台配置更加便捷,就可以把该表划分到产品模块。
银行分布式核心一般需要做水平分库。水平分库就是根据某个要素把同一个表的数据拆分到多个库里,这个要素通常叫SHARDING_KEY,应用与数据库之间使用数据路由组件,按一个固定的规则把SQL语句发送到相应的数据库节点上执行。例如以账号为要素,按固定规则把账号相关数据都分到不同个库里,在交易时也按该规则把SQL语句发送到相应的节点执行,只要交易账号相关的所有数据都该节点里,就能实现对该账号的SQL操作。
水平分库首先是实现了数据库的水平扩展,当前银行核心一般是使用oracle数据库建立RAC集群,在RAC集群下实际上是做不到数据库的水平扩展的,因为数据集群节点间的gc非常消耗资源,数据库节点越多gc越严重,所以在实际生产部署中一般只采用两个节点的RAC集群,两个节点时使用负载均衡模式时也需要小心的处理gc问题,有些系统干脆把RAC集群改为主备模式,在主节点正常工作时仅由主节点处理交易。但是,在国产数据库上我们通过水平分库可以解决数据库水平扩展。
水平分库实际上是提升了数据管理的粒度。现在主流的银行核心系统,应用是把数据库当做一个整体来看待,在应用端默认数据库里保存着全部数据,使用SQL语句能操作全部数据。在水平分库情况下需要我们把数据按某个维度进行拆分管理。例如我们用按账号维度对数据进行分库,在交易上来时我们使用账号进行路由,找到账号所在的数据库,把SQL发到该库进行数据操作。
我们在做数据移动时就需要完全知道存放账号数据的所有表,移走所有表中该账号的数据。从这个角度看我们已经做了账号级的数据管理和操作。按账号的数据路由与数据迁移,再加上账号在路由层的锁定与解锁功能,就基本实现了账号级的数据管理。实现账号级的数据管理后就能在数据移植、数据库集群故障隔离与恢复中采取更加灵活的方案。在复杂业务场景中也可以通过账号级数据的迁移把账号集中在一起,实现原来集中式数据库模式下的复杂功能,例如集团账号的资金归集、签约账号的实时补款等场景。
在分布式核心系统中,存款子系统的水平分库的字段一般采用账号或者客户号。存款子系统从本质上是电子账本系统,管理账户的金额和状态,所以按账号分库更直接一些。按客户号分库也有一定好处,首先是能把客户下所有账号划分到一个分库里,有利于同客户账号间的账务互转。
由于在实际情况中银行核心系统收到的大部分交易都是账号相关交易,上送报文里有账号没有客户号,如果使用客户号作为数据路由需要根据账号去查找客户号,然后才能做数据路由。要实现根据账号查询客户号的功能就必须有一个账号与客户号的对照关系,账号和客户号在银行系统中数据量是非常大的。两者之间的关系很难找到同时保证查询效率和高可靠性的管理方案。所以,从存款子系统接收的交易的实际情况和存款子系统的本质出发,采用账号作为存款子系统的分库字段会更高效更适合。
在分布式核心系统中,客户子系统的数据量也是比较大的,需要做水平分库。客户子系统大部分交易是以证件类型和证件号码发起的交易,但是它与存款子系统间的连接字段一般是客户号。基于上述情况,在考虑客户子系统对交易性能要求比存款子系统低些,客户子系统可以采取两次路由的方案。如果上送的是证件号码和证件类型,先通过证件号码和证件类型路由到证件与客户号对照表,在对照表中查到客户号,再通过客户号的路由和客户号查找客户相关信息。如果上送的是客户号,直接通过客户号的路由和客户号查找客户相关信息。
在分布式核心系统中,贷款子系统数据量一般不是很大,贷款账号属于账号的一个子类型,与存款账号共用介质主表。所以贷款子系统的水平分库方式应参考存款子系统。
在银行核心系统中除了存款、贷款、客户,还有现金、凭证、机构柜员、产品等模块。这些模块的数据量都不大,可以不进行水平分库。如果现金、凭证、机构柜员要进行水平分库的话,可以使用机构作为分库字段。产品是个特殊的模块,它与存款模块的耦合度非常高,产品参数实时性要求不高,产品子系统的数据可以推送到存款。
在银行核心中,存款模块又可以细分为介质层和账号层。介质是发放给客户的实体,账号是系统内部用于记账的号码。由于对外账号就是介质表里的账号,所以存款模块几乎所有交易都需要过介质层。把介质层与账号层拆分成两个子系统,就意味着存款模块的每次交易都需要多一次系统间调用。由于这两层的耦合度很高,它们解耦合的工作量也是非常大的。介质的交易只有更换介质、配介质、介质挂失、密码管理等交易,如果在整体架构设计中所有用银行卡做的交易都会过卡系统,把卡的介质管理功能放到卡系统是比较合理的。
同样的道理,存折、存单等也是介质,也可以有相应介质管理子系统。综上所述,如果考虑到改造难度,介质层留在存款子系统里,或者把介质与账号的对应关系保留在存款子系统里,把介质管理功能拆出去。如果考虑到卡上来的交易必须经过一次卡系统,把卡介质相关的功能移到卡系统,其他介质的管理功能建立独立的子系统,,把介质层从存款拆出去,这种方案工作量会比较大,但会比较彻底,也更有利于未来介质层的业务创新。
银行核心拆分成多个子系统后,交易的事物控制就变得复杂很多,对TPS(每秒交易数量)要求较高的系统一般采用saga的事物控制方式,通俗的讲就是在交易报错时使用自动回冲逻辑撤销已经成功的部分。
例如现金存款交易,它会过现金子系统和存款子系统,现金子系统负责库存现金处理逻辑,存款子系统负责分户账处理逻辑。若交易先走存款子系统,然后在走现金子系统,在现金子系统中出现了异常。现金子系统的事物是随数据库的事物回滚,但存款子系统的事物已经提交了,这就需要组合层自动发起回冲交易,撤销掉存款子系统的交易,保证整笔交易的事物回滚。自动回冲需要具备幂等性,也就是即使发送多笔回冲后最终也只成功一笔。
为了解决当前MYSQL数据库单表数据量不能太大的问题,可以采用水平分表来降低单表数据量。由于水平分表与水平分库的路由方式和拆分规则非常相似,上述对水平分库进行了详细的论述,水平分表就不再进行赘述。
二、分布式银行核心应用架构
在上述分布式业务架构概述中,已经大致说明了分布式核心各子系统的特点和它们可以采取的分库分表方式。接下来将进一步论述这些子系统怎么组合成一个功能齐全的分布式核心系统。由于把原来一个整体的集中式核心拆分成了多个子系统,要实现原来的功能就需要一个可以做流程控制的系统来组合子系统的功能,这个做流程控制的系统在架构上叫组合层。组合层负责接收柜面、自助渠道以及其他渠道过来的交易,通过流程控制组合各子系统的原子功能,以实现一个完整的业务功能。分布式核心联机交易架构如图2-1 :
图2-1 分布式核心联机交易架构
1)组合层
上述架构图中,服务检查和机构柜员检查属于组合层交易前处理逻辑,检查逻辑连接组合层公共数据库获取交易配置和机构柜员信息,用于校验交易和机构柜员的合法性。组合层的交易流程其实是一个接口,这个接口负责接收外围请求,按流程远程调用原子服务。这个流程需要可视化编排,然后把编排结果生成代码。
银行核心系统交易传参较非常多,尤其是存款子系统的“开销存取转”及销户转开,参数多达几十个、甚至上百个,可视化的流程编排会大幅提高服务开发效率,在运维阶段也能防止交易复杂度太高,不好维护。
可视化编排的流程生成代码后可以当做一个接口来管理和部署,这些接口可以按业务条线建立目录进行管理。组合层中的公共子服务可以连接组合层的公共数据库,提供一些在流程控制或交易控制使用频率较高的逻辑,例如机构关系检查、产品分支判断等。组合层中的公共子服务也可以做一些在流程上配置比较难实现的逻辑,例如查询结果集合并、循环或递归调用等。
在组合层中流程之间原则上不应进行相互调用,防止系统变成‘牵一发而动全身’的网状结构系统。做到了这点的话,组合层的各模块也是可以独立部署、分业务条线管理的。
由于做了水平分库,在转账交易中借贷双方账号可能不在一个库里,这需要我们对所有的转账交易进行借贷分离的逻辑拆分。也就是原来在一个接口里实现的借贷双方记账的逻辑,现在拆成借和贷两个接口,在组合层先调借后调贷。
为了实现事物最终一致性,在组合层需要实现自动回冲。为了实现自动回冲,需要在组合登记交易主表和交易步骤表。交易主表每笔交易登记一笔,记录交易要素和交易状态。交易每调用一次原子服务层,交易步骤表登记一笔,记录该次调用的要素和状态。
当组合层的流程调用原子服务层失败或调用成功后原子层交易处理不成功,组合层流程查询交易步骤表,获取该笔交易的所有成功的步骤倒序回冲。对某一步骤的回冲一般是上送该笔交易的流水号和步骤号,调用该步骤的回冲接口。在回冲接口中需要根据流水号和步骤号查询子系统的流水表获取原交易要素,使用原交易要素执行回冲逻辑。
2)原子服务层
从架构图中可以看到,存款子系统和客户子系统是水平分库的,其他都是垂直分库的模块。水平分库的子系统需要有一个公共库,用于存储该子系统的参数表。水平分库的子系统中需要有数据路由组件进行数据路由。程序中SQL语句通常会关联公共库与分库进行关联查询,这时会涉及跨库查询,通常拆分成两次查询就能解决跨库查询问题。在分布式系统中也通常会采用在分库中冗余存储参数表的方式来避免跨库查询。不过,在实际生产中出问题的语句通常是一些关联比较复杂的语句,所以通过多次查询,把语句简单化是解决跨库问题较好的方式。
在原子服务层中产品模块是一个较特殊的模块,其管理维护是由参数平台负责的,参数数据主要是存款模块使用。参数维护实际上是按版本维护的,通常是参数人员配置好一版参数后提交给测试人员,在测试环境测试通过后才上生产。基于这种参数管理的情况,参数数据可以通过推送的方式,按版本更新到存款公共库里。
在原子服务层中,由于有黑名单等限制或检查类数据,对实时性要求不高,也可以由相应模块推送到存款公共库。通过数据推送的方式减少应用间的调用次数会较大的提高交易效率。
原子服务层是分布式核心系统中业务实现层,为组合层提供按模块解耦后的,具有单一业务意义的原子服务接口。例如现金取款交易,组合层先调用存款子系统的取款原子服务接口,然后调用现金子系统的现金取款处理接口。为保证服务的原子性,现金子系统中的现金取款处理接口应只包含现金取款相关的现金处理,不应当用现金主控来提供综合的现金借和现金贷的处理。原子服务层的其他服务接口也应准守相应的规则。
在当前主流银行核心中,批处理一般分为日终批处理和日间批处理,日间批处理一般是指文件形式的代发代扣,批处理的重点和难点主要是日终批处理。日终批处理逻辑非常复杂,其主要的日终步骤有过账、计提、结息,还有很多其他步骤,例如开户处理、挂失处理、凭证处理、约定账务处理、报表生成、数据整理提供、数据清理等等。
在日终流程图里通常会有几十个甚至上百个步骤。由于夜间联机交易相对较少,系统剩余资源相对充足,所以通常会把日间不便于处理或需要日期改变才能处理的逻辑放到日终。在集中式系统中批量处理比单笔处理会快很多,部分需要进行数据加工处理的逻辑也放到了日终,例如业务状况报表、对账单等。由于数量大、产品复杂、日终步骤多等因素,当前主流银行核心日终的复杂度已经非常高,导致日终易报错、日终耗时不可控,特别是季度结息的时候。
在集中式核心的日终批处理步骤中,一般是先获取待处理结果集,然后循环结果集,在循环内逐条执行处理逻辑。但在银行分布式核心系统中,对数据进行了水平分库和垂直分库,很难快速获取全量处理结果集,也不能在一个子服务里跨各子系统处理数据,我们只能通过组合层调度各子系统。所以在分布式核心中只能把批量交易拆成联机交易,类似于“批转联”。就是把日终步骤中循环里的单笔处理逻辑封装成联机接口,把获取待处理数据集的逻辑写入任务管理层,在任务管理层中把数据集逐条封装成请求报文发送到联机交易接口中。
基于上述情况,在银行分布式改造中,日终批量的架构图需要进行重新设计。如图 2-2:
图2-2 日终批量的架构图
在上述日终架构图中一共分为三层,分别是控制层、组合层、原子服务层。也可以理解为在联机交易的架构图上加一个控制层。控制层里又分流程控制层和任务管理层。
1)流程控制层
在当前主流银行核心中,日终是按照日终流程图执行的,流程图中有有串联也有并联,整个图是非常复杂的,在分布式银行核心系统中,需要做“批转联”的是日终流程图中的每一个步骤,每个步骤内的处逻辑拆成了联机接口来处理,实现并发处理。
但是,每个日终步骤之间还是需要按日终步骤流程图执行的,日终步骤流程图中并联的部分可以并发执行,串联的部分需要按顺序执行,控制日终按流程图执行就是流程控制层主要的功能。
2)任务管理层
任务管理层主要有如下功能:
链接各数据库获取待处理数据集,把待处理数据集封装成请求报文集,每条待处理数据封装成一个报文。水平分库的逐个库查询待处理集并处理。这里需要注意的是待处理数据集非常大时,需要分批处理或逐库处理,才能避免JVM内存溢出。其中待处理数据集包含多种,例如:帐号集、客户集、机构集、柜员集等。
并发发送请求,并记录结果。请求报文参数根据日终作业的单笔处理逻辑需要而定。
3)组合层和原子服务层
在日终批量架构图中,组合层和原子服务层就是联机交易架构图的组合层和原子服务层,在此就不再赘述。
4)日间批处理
日间批处理一般就是文件批量,文件批量只需要把文件里的待处理记录当做待处理数据集,每条都封装成一个联机报文,并发发送到组合层,后面的处理逻辑就跟联机交易完全一样,这里就不再赘述。
三、分布式银行核心数据管理
在水平分库子系统中,由于数据分布到了多个数据库中,程序在执行SQL语句时需要确定SQL发到那个库执行。一般情况下是在数据路由中间件中配置一个路由字段,在SQL语句中添加一个该字段的条件,数据路由中间件根据该字段对应的值路由到数据所在的库里。
我们以账号作为分库要素(SHARDING_KEY)具体分析存款子水平分库的细节。在存款子系统中,只有部分表有账号字段,大部分表都是保存的账号ID或内部账号ID,为了直接高效的路由,我们需要在没有账号的表里建立一个账号字段用于路由,但这样做会把带有业务含义的账号用于架构层的数据路由,这可以说是违反设计规则的。
最佳的做法应该是在每个需要分库的表里都添加一个SHARDING_KEY字段,把账号转换成一个业务无关的字符串,保存在这字段里。在没有账号的表中通过关联关系找到账号,把账号转换后存放在SHARDING_KEY字段里。
通过SHARDING_KEY找到账号对应的库,中间需要经过一个算法。这个算法可以是对账号求hash,再取绝对值,然后对分库个数取余,这样得到0至N的正整数(N为分库个数-1),我们把分库编号为0至N,这样任何一个SHARDING_KEY都对应着唯一的分库。这就实现了简单的数据路由规则。由于上述算法需要对分库个数取余,这就意味着数据扩容时,分库个数会增加,会改变原有数据的路由结果,导致数据找不到其存放的库,所以上述路由算法是不支持数据库节点在线扩容的。
为了支持数据库节点在线扩容,还需要加入上述算法取余的余数与数据库编号的对应关系,在扩容过程中通过改变对应关系表,以达到在线新增数分库节点的目的。具体做法是对SHARDING_KEY求hash取绝对值后,对100取余,获得0到99的正整数,这些正整数按范围对应到相应的分库上,假设0到9对应0号库,10~19对应1号库,以此类推。这样我们在新增分库节点时仅需修改对应关系就可以把某一段余数值对应的账号移动到新节点上。数据路由示例,如图3-1:
图3-1 数据路由
在数据路由中,还要一个例外路由登记表,用于登记需要进行指定路由的账号,数据路由组件需要先检查例外路由登记表,若表中存在指定路由信息,则按该信息进行路由。例外路由信息表关键字段有两个,一个是路由原来路由账户,另一个是指定路由号,在一般交易场景中,指定路由号也是一个账号。
例如在配介质交易中,新介质和原介质都对应着一个内部账号ID,所以新介质需要使用原账号计算SHARDING_KEY进行路由。这账号的对应关系就是需要登记在例外路由登记表里。考虑到路由效率,例外路由登记表不宜过大,而且在一个原子服务中查询一次例外路由表后使用交易级缓存把结果保存下来,以备交易全过程使用。例外路由表可以放在存款公共库里,由存款子系统数据路由组件使用和维护。
为实现按账号级粒度管理数据,在线数据移动是最重要的功能,做到账户级数据的在线可靠移动,能为系统管理和功能实现带来很多方便。账号级数据在线移动的前提是所有多账号的交易都已经拆分成了两个或多个原子服务,总之就是要保证每个原子服务只处理一个账号的逻辑。
在存款子系统中最重要的是借贷分离,借贷分离后能保证大部分原子服务只读写一个账号,我们移动数据后改变账号分布就不会影响交易逻辑。在账号移动过程中针对明细数过多的账号可以进行明细结转,以控制明细条数并且保证余额连续性。对资金归集、签约自动补款等场景,需要把具有实时资金自动划转的账号群当做一个整理移动。在按账号数据移动时,需要对账号或账号群在路由层进行暂时的锁定,锁定其实就是登记一张锁定表,数据路由查询锁定表是否存在记录,若存在则拒绝交易或等待合理时间后从新调起交易,考虑到效率锁定表也需要做交易级缓存。迁移完成后更新路信息,解除锁定。锁定表可以保存到存款公共库里,由数据路由组件使用和维护。
数据移植是指从老系统中把数据移到新系统,当前主流银行核心数据迁移是停机进行的,数据迁移和报表核对完成后新系统启动,开始做绿灯测试和补挂失冻结等,然后开放渠道交易和网点正式营业。整个过程一般耗时2至3天,这段时间的停机窗口需要申请报备,如果在这个时间窗口内数据移植或新系统运行出现短时间无法解决的问题,并且还未接入交易。只能宣布上线失败,退回原系统继续开展业务。如果已经接入交易,在新系统发生不可短期解决的问题,会产生严重后果。这就导致当前主流核心的上线过程,新老系统切换风险非常高,没有容错机会。
在银行核心分布式系统的存款子系统中,由于数据管理的粒度精确到了账号级,如果在加上一些控制机制就可以实现在线数据迁移。总体策略是数据移植过程中让新系统和老系统同时运行,交易请求复制两份同时向两个系统发送,若老系统交易失败则回冲新系统交易,若老系统成功新系统失败则找出失败原因,并且删除新系统中该账号的数据,修正逻辑保证新系统交易结果与老系统一致后在重新迁移。
复制交易同时发向两个系统的功能需要交易路由层来实现,交易路由时需要判断交易的账号是否已经迁移到新系统,只有迁移到新系统的账号上来的交易才发向新系统,没有迁移的则只发老系统。这样在数据迁移过程中新系统接收到的交易会逐渐增多,过程中可以动态观察新系统运行情况,逐步进行必要修改和性能优化,当所有账号都迁移到新系统中后,运行核对逻辑,逐个账号核对数据是否正确。在确保数据正确,新系统运行正常后停止向老系统发送交易,然后就完成了系统切换。
要做到完全不停机还需要在线插入交易路由层,当前主流银行核心一般都是通过F5做的负载均衡。可以调整F5的发送地址池来把交易引向交易路由层,通过路由层再把交易转向后台系统,这个过程也是可以使用F5逐步转移交易的,如果交易路由层宕机,F5可以通过心跳监控发现,立刻把交易切回原路径,这个过程也是渐进可控的。按照上述方案可以做到不停机,在线数据移植。不过,使用这种方案除了系统要做到账号级数据管理,还需要把新老系统在同样场景下运行结果不一样的需求,放到系统切换后做二期上线,这样的需求通常占比很少,只有这样才能按上述方案进行在线数据迁移,低风险系统切换。
在数据迁移过程中对于数据量比较大的明细类数据,可以按日期对历史明细提前迁移,之后在线实时迁移过程中只迁移增量明细。但是,迁移大量的历史明细类数据进入新系统本身不是一个好的策略,历史明细类数据可以迁入历史查询平台。对于账号明细,还需要进行余额结转,以保证余额的连续性。
在分布式模式下,单个节点的可靠性其实比集中式低很多,分布式的优势在于其节点较多,可以做到故障隔离,避免整体宕机。下面我们对分库情况下mysql主从模式集群故障的处理展开讨论。
当一个分库的mysql从节点宕机时不会产生太问题的,直接同步数据,然后挂载重节点,slave就会自动追平数据。当分库的mysql主节点宕机时,处理起来会比较麻烦,由于通常情况下mysql主从关系不保证主从节点数据是完全一致的,它与oracle数据库的RAC集群是不一样的,mysql这样做规避了gc问题,提供高了效率。但当主节点宕机时,由于主从节点数据不一定一致,就不能直接启动从节点接管交易的数据操作。如果按照上述方案,我们实现了账号级的数据管理,并且按照上述应用架构图设计了组合层,做了借贷分离保证每个原子服务只操作一个账号的逻辑,我们可以根据组合层流水中登记的账号,通过路由算法可找出发向故障分库的所有流水,与故障分库从节点现有的流水做对比,找到从节点缺少的流水和对应的账号,锁定这些账号,然后从库切换为主库从新提供服务。最后人工恢复主库,同步差异账号的数据,然后解锁账号。在主库无法恢复的情况下可以根据组合层登记的交易要素发送补账交易。
上述对比流水找到差异账号的过程可以通过运维程序自动实现,由于mysql主从同步之间的数据不会差太长时间的,可以只取一个时间段的流水做对比。上述是从业务系统设计的角度来考虑数据库故障恢复,随着国产数据库逐渐成熟后,他们能比较好的解决性能、可靠性及故障恢复问题。
四、分布式银行核心系统设计性能考量
在银行核心分布式系统中,由于把各业务模块拆分成了子系统,一次交易通常需要调用多个子系统才能完成,交易链路变长,交易耗时可能会有一定的增加。所以银行分布式核心在设计的时候需要对性能有一定的考量。
缩短调用路径是最根本的解决方法。通过数据库推送的方式可以缩短调用路径,例如:把产品子系统的信息推送到存款公共库,存款直接通过访问数据库的方式获取产品参数,还可以把黑名单、白名单等量不是特别大的信息推送到存款公共库。
合理拆分子系统,确实做到高内聚低耦合,不为了拆而拆,不过设计,也能降低跨系统访问频次。例如存款模块里的介质层和存款层,因为卡交易必须过一次卡系统,把卡介质管理功能放到卡系统比较合适。如果没有卡系统配合新增介质管理相关的功能,介质层和存款层拆分后所有账务交易都会多一次跨系统访问,从性能方面考虑是不太合适的。还有其他一些模块从业务上都是可以进行进一步分的,但是细分的程度度需要从性能角度进行考量。
除了缩短交易路径和降低系统访问频次,还可以减少跨库访问来提高交易效率。在水平分库情况下,参数表在该模块的公共库里,参数表与主表关联的查询,可以拆分为两步查询。例如查询账户利率,可以先查询主表获取产品号,再根据产品号到公共库查询利率关联表、利率浮动表及利率计划表等相关利率表。
在水平分库情况下,使用非分库条件查询,可能会涉及到跨库。例如按账号水平分库,使用流水、客户、机构、柜员等信息查询。这种情况可以按精确查询和非精确查询分类处理,非精确查询可以采用异步方式去逐库获取信息,然后合并查询结果。例如机构/柜员开销户查询、产品汇总信息查询、流水汇总信息查询等。这些交易在当前主流核心中一般都是多条件任意拼组的查询交易,这类交易需要拆分为两支查询交易,一支需要输入精确查询条件,另一支无需输入精确查询条件,可以从业务制度上控制非精确查询交易的使用权限和频率,这样可以大大降低系统开销。
从技术架构设计方面考虑,可以使用全局缓存和交易级缓存来提高数据访问效率,全局参数表存入全局缓存中使用更新机制定时更新。在交易过程中缓存需要多次使用的实时性要求不高的对象,避免多次查询同一行记录,例如账户主表和介质主主表的非金额类信息。
如果交易对耗时比较敏感,例如与支付宝微信等对接的记账接口可以抽出所有交易检查,采用并发方式同时发起检查,然后收集检查结果并判断,再进行金额处理。
五、分布式银行核心系统交易场景推演
当前分布式银行核心系统相关的技术和业务还没有成熟的方案。所以在系统建设过程中没有现存可靠的方案可以参考,需要进行新设计。新设计的方案无论考虑的多周到,都会有一些的缺陷,要想减少这些设计缺陷,需要进行业务推演。下面将针对存款和客户子系统具有代表性的交易进,按照上述联机交易和日终批量架构图进行分析推演,为了简单起见,我们先不拆分介质层与存款层,这样也不用考虑卡系统与存款之间的访问。
活期现金开户:在交易界面上快查访问ECIF系统,提交交易先进组合层,过存款子系统,直接从存款公共库里获取黑名单和白名单进行校验,在存款公共库获取开户产品相关信息进行带金额开户,并且登记揽存信息。然后过现金子系统进行库存现金处理。
活期转账开户:在交易界面上快查访问ECIF系统,提交交易先进组合层,过存款子系统做借方账号取款处理,然后从存款公共库里获取黑名单和白名单进行校验,在存款公共库获取开户产品相关信息进行带金额开户,并且登记揽存信息。
定期现金开户:与活期现金开户类似。
定期转账开户:与活期转账开户类似。
活期现金取款:交易进组合层,先过存款子系统做借方记账,然后再过现金子系统做现金处理。
活期现金存款:交易进组合层,先过存款子系统做贷方记账,然后再过现金子系统做现金处理。
活期转账:交易进组合层,先过存款子系统做借方记账,然后再过存款子系统做贷方记账。
现金汇款:交易进组合层,先过存款子系统做内部账记账处理,再过现金子系统做库存现金处理,最后发汇款系统。
转账汇款:交易进组合层,先过存款子系统做借方记账,然后发汇款系统。
定期转账部提:交易进组合层,先过存款子系统做借方记账,然后过存款子系统做贷方记账。如果存在收息账号,需要再过一次存款子系统对利息进行处理。因为定期部提会涉及新开内部账号ID,如果是存单的话会重打存单。内部账号ID新开时需要使用原账号进行数据路由。
活期转账销户:交易进组合层,过统一签约或存款子系统签约模块检查是否存在阻止销户的约,过存款子系统进行带金额销户,再过存款子系统做贷方记账。
定期转账销户:与活期转账销户类似,如果有收息账号需要再过存款子系统对收息账号记账。
更换介质:交易进组合层,在更换介质交易前处理中锁定原账号,调用存款子系统原账号检查逻辑并且更新状态,然后再调用存款子系统进行新开介质操作,再调用账号移动功能把原账号对应的所有数据移动到新账号所在的分库中,然后过存款子系统解锁原账号。由于介质查询需要既能根据原账号查询又能根据新账号查询,所以需要使用两个账号登记两条换证登记表。两条记录是分别使用新账号和原账号进行数据路由登记的。
配介质:交易进组合层,过存款子系统使用主介质账号作数据路由,在主介质分库里新开介质,然后调用数据路由组件登记一条例外路由记录,把新介质的路由账户设置成主账号。
大额存款转让:交易进组合层,过存款子系统做借方记账,再过存款子系统做贷方记账,然后再过存款子系统做大额存单相关处理。
按流水号查流水:在做好数据字典规范的情况下,可以直接查询组合层登记的交易主表获取信息。但由于组合层登记的交易数据量会比较大,需要考虑按流水分库的情况,并且需要考虑数据清理的情况。
按流水号查账号明细:交易进组合层,在组合层里根据流水查询该笔流水对应的账号,调用存款子系统根据该账号做数据路由查询交易明细表。
一借多贷和多借多贷:交易进组合层,循环调用存款子系统进行单边记账,当余额增加的记账时,需要同时冻结新增金额。记账完成后,循环调用存款子系统对金额进行解冻。这样能保证新增的金额不被取走,保证记账失败后能做回冲。但是需要新增一种独立的冻结类型,在扣划和超额冻结的时候去除这部分冻结金额。
取款交易实时补款:当账号签了实时补款协议并且签的是活期账号,取款的时候余额不足时会从签约账号自动扣款补到交易里,让该笔取款交易成功。由于补款账号与签约账号可能不在一个库里,没法记账。若返回需补款金额,让组合层调用签约账号扣款,这种补款签约可能有多层,还可能是递归的,在组合层调度会非常复杂。所以在做这种补款签约时需要把带有签约关系的账号移动到一个分库里,移动账号后登记例外路由表,强制账号路由到指定分库里。移动账号时需要注意判断被移动账号是否已经存在例外路由表里,若已经存在需要把与其存在关系的账号全部移动,并且修改例外路由表。
集团账号实时资金归集:与取款交易实时补款处理方式类似,也是需要在签约时移动数据到一个分库里并且登记例外路由表。把有签约关系的数据都移动到一个分库里并且数据路由也强制到一个分库里,这种方案可以实现在集中式核心系统里的复杂业务逻辑。
家庭账号:家庭账号的处理方式类似于集团账号。
限额:目前常用的有账号层累计限额和单笔限额,按渠道的账号层累计限额和单笔限额,由于我们一个账号的所有相关数据都在一个库里,所以账号层限额是没有问题的。还有客户层限额,例如外币限额,可以把客户限额放到客户子系统,在外币交易时过客户子系统检查和登记限额。
手续费:在银行核心中手续费代码关联科目,在参数平台设置好交易级、账户级、客户级、机构级、渠道级等场景中的手续费或手续费包,按版本推送到存款公共库。在交易中判断交易符合的场景,根据场景获得对应的手续费或手续费包,再根据手续费或手续费包对应的金额收取费用,如果是现金收费就需要多过一遍过现金子系统,是转账收费就需要多过一遍存款子系统。
关系定价:在核心系统中关系定价是单客户、客户群组、客户级别等主体与利率之间的关系。关系定价相关数据应由产品参数平台维护,按版本推送到存款公共库。在开户、部提、销户等交易中根据交易主体和交易要素定位具体的利率。
按照上述对客户子系统的路由规则定义,客户子系统采用复合的路由规则。当上送证件类型和证件号码时,采用证件类型和证件号码路由。当上送客户号是采用客户号路由。
建立客户信息:交易进组合层,调用客户子模块根据证件类型和证件号码进行数据路由,登记证件类型和证件号码与客户号关系表,然后根据客户号进行数据路由,登记其余客户相关表。
客户合并:前台根据证件类型和证件号码快查到合并与被合并客户号,交易进组合层,使用合并与被合并客户号调用存款子系统,更新账号的客户信息,然后调用客户子系统修改证件与客户号对应关系表及被合并客户号的其他信息。其中按客户号查找存款分库是没有分片键的查询。
存款子系统查询客户信息:在存款子系统中通常只保留账号对应的客户号,根据账号获得客户信息,只能通过账号调用存款子系统查询客户号,再通过客户号调用客户子系统查询客户信息,也可以根据客户号调用ECIF系统获取客户信息。
六、分布式的系统的特点
集中式系统与分布式系统有着本质的区别,集中式系统强调单节点的处理能力和稳定性,分布式系统是从整体来衡量系统的处理能力、稳定性、可扩展性。
对于可扩展性,集中式系统是靠剩余的系统资源来保证未来的资源需求,分布式系统可以逐步进行横向扩展来满足系统资源需求。不过要实现不停机的横向扩展,在系统技术设计和业务设计的时候要有充分的考量,不是所有分布式系统都能进行横向扩展,上述方案中提到的最简单的数据路由规则就不能实现数据库横向扩展。
对于稳定性,集中式系统靠的是使用高端服务器和可靠数据库及应用组件。分布式需要做到快速的故障隔离与恢复,这一点需要在技术和业务设计上做充分的考虑,上述方案中提到的数据管理粒度提升到账号级别就是为了故障隔离与恢复打基础的。局部压力可转移也是分布式系统稳定性要求的必备功能,只有做到局部压力可转移才能实现全局可调控的负载均衡,应用集群节点的压力转移可以用交易路由实现,数据库集群节点的压力转移是需要进行数据移动来实现的,这也需要能直接管理账号级数据。防止雪崩也是分布式系统必备的功能,它的具体做法就是大家经常提的降级和熔断,还有提升回冲交易的性能也能降低雪崩的风险。
在分布式系统中,应用节点应可以进行快速部署新增,数据应当按一定的粒度被管理起来,在这个粒度上数据应可以锁定、解锁及数据节点间移动。只有应用可灵活部署、数据可流动,我们的分布式系统才是一个活的系统,才能真正解决集中式中我们遇到的问题,不然仅仅是将多个集中式系统放到一块来运行。
从集中式系统迈向分布式系统过程,是IT系统由单节点工作变成群体协同工作的过程,工作的效率除了取决于每个节点的能力,还取决于整个群体的协同效率,设计分布式系统时需要充分考虑协同效率。在群体协同工作时,好的管理机制能大幅提高协同效率,所以分布式系统需要完善的管理系统才能把它运行好。
集中式到分布式的演变,是IT系统形式的改变,也是人们对IT系统管理和使用理念的改变。同样,也只有改变理念才能研发、使用和管理好分布式系统。
时代给予传统金融业的危机感从未停止过,不论是互联网的冲击,还是疫情引发的新一轮挑战。为此Gdevops全球敏捷运维峰会北京站精选出近10家银行的金融科技探索,分享其在中台建设、数据库迁移、运维转型上的实战经验,助力Fintech战略落地。部分主题:
中邮消费金融:《建设敏捷型消费金融中台及云原生下的DevOps实践》
建信金科:《银行数字化转型战略分析、关键技术及未来架构趋势》
平安银行:《平安银行“传统+互联网”混合CMDB及运营中台实践》
中国银行:《银行日志监控系统优化手记》
工商银行:《ICBC的MySQL转型探索之路》
农业银行:《中国农业银行信贷中台及数据中台建设实践》
民生银行:《民生银行智能运维平台实践之路》《民生银行在SQL审核方面的探索和实践》
蚂蚁金服:《OceanBase分布式数据库在西安银行的落地和实践》
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721