1. 画图之前,先把“核心”这两个字掂量清楚
做核心系统这行十几年,最怕听到一句话:“你先给我画张图,把核心系统讲明白。”画图本身不难,难的是同一张图要同时让三类人看懂:业务方要看到产品怎么配、账怎么走;技术方要看到交易怎么落、库表怎么切;运维和测试要看到日终怎么跑、出问题从哪查。一张图要撑住这三种视角,就必须先想明白核心系统到底在银行里扮演什么角色。
用一句最直白的话概括:银行核心系统就是全行唯一有权改动“钱”的地方。渠道、支付、信贷、风控、报表、数据仓库,这些系统手里都有数据,但它们手里的余额只是“反映”,不是“权威”。客户在手机上看到的余额、柜面打印的存单、对账单上的数字,最终都要回到核心系统里那本账上对。所以核心系统在业内还有一个更准确的说法——账务权威源。任何一笔资金变动,别的系统可以发起、可以校验、可以展示,但只有核心系统能把这笔变动“记进去”,并且保证借贷平衡、总分一致、可追溯、可回滚。
这个定位一旦立住,图就好画了。因为核心系统的所有模块、所有流程、所有参数,本质上都在服务四件事:第一,把客户和产品描述清楚,也就是“谁、用什么产品、开了什么账户”;第二,把每一笔资金变动翻译成会计语言,也就是“借哪个科目、贷哪个科目、金额多少”;第三,把这些分录汇总成总账,让全行的资产负债能对上;第四,在每天结束的时候把所有该算的利息、该提的准备、该结的账一次性算完,保证第二天开门时账是平的。
不少刚接触这块的同学会把核心系统理解成“一个大号的存款记账程序”,这个理解偏差会直接导致画出来的图缺胳膊少腿。存款只是核心系统里最显眼的一块,贷款、总账、内部户、产品参数、计息结息、日终批量、对外接口,缺了任何一块,这张图都跑不通一笔完整的业务。我见过一些方案文档,画了满满一页的渠道和微服务,唯独没画总账和内部户,结果评审的时候被一句“这笔钱从哪个科目出”问得哑口无言。
所以这张图的第一层,不是技术架构,而是账务架构。先有账,后有系统;先有科目,后有服务。这个顺序搞反了,后面拆微服务、选数据库、做单元化,全是空中楼阁。接下来的内容,我会按“总览图怎么分层—模块怎么拆—一笔交易怎么走—利息怎么算—分布式怎么改—问题怎么查”这个顺序,把这张图一层一层摊开讲,中间穿插参数计算、实操细节和踩过的坑,尽量做到你看完能自己动手画一张,并且能拿着它去跟业务和技术两边对话。
2. 总览图的第一刀:按“账”分层,而不是按“系统”分层
2.1 六层结构:从渠道到总账,每一层只干一件事
我习惯把核心系统的总览图切成六层,从上到下依次是渠道接入层、交易路由层、产品服务层、账务处理层、总账汇总层、数据服务层。这个切法的好处是每一层职责单一,出问题时能快速定位是哪一层的责任,而不是一上来就翻几万行代码。
渠道接入层负责协议转换和报文校验,柜面、手机银行、网上银行、自助设备、批量代发、第三方支付机构的指令都从这里进来。这一层不碰账,只做“翻译”和“初筛”,比如报文格式对不对、必填项全不全、签名验没验过。很多生产事故就出在这一层没卡住,脏数据一路穿到账务层,最后只能靠冲正收拾。
交易路由层负责把请求分发到对应的业务处理单元,同时做限流、熔断、灰度分流。这一层的关键是“路由规则要可配置”,比如按产品码路由、按客户等级路由、按机构路由。规则写死在代码里,后期做产品差异化或者迁移切流会非常痛苦。
产品服务层是业务逻辑的主战场,存款开户、存取款、贷款放款、还款、转账、冻结、解冻都在这一层。它的输入是交易报文,输出是“账务指令”,也就是一组待记账的分录。注意,这一层只负责“算出该怎么记”,不负责“真的去记”,这个边界一定要划清楚。
账务处理层是真正动余额的地方,负责分录落库、余额更新、账户状态变更、流水登记。它的核心要求是强一致、可幂等、可冲正。所有对余额的修改必须在这一层完成,任何绕过这一层直接改库的操作,都是给自己埋雷。
总账汇总层负责把分户账的分录按科目汇总,生成总账余额,做总分核对。这一层是财务口径和业务口径的接缝处,也是月末季末最容易被追问的地方。
数据服务层负责对外提供账户查询、流水查询、对账单下载、报表抽取。它读的是账务数据,但不改账,所以可以做读写分离、可以做缓存,压力大也不至于影响记账主链路。
这六层画在一张图上,横向再按业务域切一刀,就是客户域、存款域、贷款域、支付结算域、总账域、公共域。纵向分层解决“职责”,横向分域解决“归属”,两刀切完,一张图的骨架就有了。
2.2 三条主线:交易流、账务流、数据流必须分开画
骨架有了,接下来要画“流”。我见过太多图把交易流和账务流画成一条线,结果看图的人以为记账是同步完成的,一做压测就发现不对。实际上,一笔业务在核心系统里至少有三条独立的流在跑。
交易流是实时的,从渠道进来,经过路由、校验、业务处理,最后返回结果给客户。这条流追求的是响应时间,通常要求几百毫秒内完成。账务流是交易流的子过程,但它关注的是借贷平衡和分录完整性,一笔交易可能产生两条、四条甚至更多分录,这些分录必须全部成功或者全部失败。数据流是异步的,日终批量、报表抽取、数据仓库同步都走这条线,它追求的是吞吐量和最终一致。
把这三条流分开画,好处是能一眼看出哪些环节是同步阻塞的、哪些是异步解耦的。比如转账交易里,扣款和入账必须在同一个事务里完成,这是同步的;而短信通知、积分累计、报表登记,完全可以异步做。如果图上把这几件事画成一串,说明设计上还没想清楚哪些能松、哪些必须紧。
注意:三条流分开画,不等于三套代码。很多团队为了图好看,把交易流和账务流拆成两个服务,结果联机交易里出现了跨服务事务,反而把简单问题搞复杂了。分层的目的是理清职责,不是制造调用链。
2.3 一张图里必须出现的“隐性模块”
除了上面那些显眼的模块,核心系统里还有几块东西业务方平时看不见,但图上不画就会漏掉关键逻辑。第一块是参数管理,产品码、利率码、费用码、科目映射、机构参数、渠道参数全在这里,参数配错导致的生产问题占比相当高。第二块是内部户管理,应收、应付、过渡、垫款、手续费收入这些内部账户,是联机交易能平衡记账的关键。第三块是批量调度,日切、计息、结息、计提、摊销、总分核对、报表生成,全靠它串起来。第四块是权限与授权,大额交易复核、冲正授权、参数修改审批,这些控制点不画出来,审计那一关过不去。
这四块内容在图上通常放在侧边或者底部,用虚线连到主流程,表示它们是“支撑性”的。我建议画图的时候给它们留足位置,因为实际排查问题时,十次有六次最后查到的是参数、内部户或者批量顺序上。把这些隐性模块显性化,图才真正具备“排查手册”的价值,而不只是一张汇报用的架构示意图。
3. 模块拆解:客户、产品、账户、总账,四块积木怎么搭
3.1 客户信息与账户分离:一个客户号撑起所有关系
核心系统里第一个要讲清楚的概念是“客户与账户分离”。客户信息记录的是“人”或者“单位”,包括客户号、证件类型、证件号码、联系方式、客户等级、风险标识;账户记录的是“钱”,包括账号、产品码、币种、余额、状态、开户机构。一个客户可以开多个账户,一个账户只属于一个客户,这是最基础的一对多关系。
为什么要分离?因为很多业务规则是挂在客户身上的,不是挂在账户上的。比如同一客户在本行的所有账户合并计算限额、同一证件号不能重复开一类户、客户等级决定费率优惠。如果客户信息和账户信息揉在一张表里,这些规则实现起来会非常别扭。
画图的时候,客户域通常放在最左边,用一个方框表示客户主档,下面挂客户关系、证件、联系方式、客户标签几个子块。客户域和存款域、贷款域之间用“客户号”这条线连接。这条线看着简单,但它是后面做统一视图、做反洗钱监测、做额度共享的基础。我见过一些老系统,客户信息散落在各个业务模块里,同一个身份证号在不同模块对应不同的客户号,后期做客户合并花了整整一年,这种教训值得记在图上。
3.2 产品工厂:参数化程度决定核心系统的天花板
产品工厂是核心系统里最能体现设计水平的一块。所谓产品化,就是把“活期存款”“整存整取三年”“通知存款”“个人消费贷款”这些业务形态,抽象成一组可配置的参数,而不是每个产品写一套代码。产品码是主键,下面挂利率参数、计息方式、期限规则、费用规则、账户规则、交易控制规则。
我举个具体的参数组合来说明它有多细。一个定期存款产品,至少要配:币种、起存金额、存期、利率码、计息基准(360还是365)、计息方式(单利还是复利)、结息方式(利随本清还是按期付息)、提前支取规则(靠档还是活期)、逾期规则、自动转存标志、是否允许质押。这些参数里任何一个配错,算出来的利息就是错的,而且错得很隐蔽,往往要等到客户投诉才发现。
产品工厂画在图上,通常是一个大方框下面挂一排小方框,每个小方框是一类参数。旁边用箭头指向“账户开户”和“交易处理”,表示产品参数在开户时被实例化到账户上,在交易时被读取使用。这里有个关键设计点:账户开户时要把产品参数“快照”下来。因为产品参数会调整,如果账户不存快照,每次计息都去读最新参数,那存量客户的利率会跟着新产品一起变,这在业务上是不可接受的。快照这个动作在图上往往被忽略,但它是利息计算准确性的前提。
3.3 账户模型:余额不是一个数,是一组数
新手最容易犯的错误,是以为账户余额就是一个字段。实际的核心系统里,一个账户的余额至少包括账面余额、可用余额、冻结余额、透支额度、上日余额、当前余额几个维度,有的还会区分已入账未起息、已起息未到期。
账面余额是会计口径的实际金额,可用余额是客户能动的钱,两者之差通常就是冻结金额和未达账项。举个大额存单质押的场景:客户账户里有100万,质押冻结80万,账面余额还是100万,但可用余额只有20万。如果图上的账户模型只画一个“余额”字段,这个场景就没法解释。
账户状态同样是一组标志位,正常、冻结、只收不付、只付不收、睡眠、销户、久悬,不同状态对应不同的交易准入规则。画图时我建议在账户方框里把“余额结构”和“状态机”作为两个子块单独画出来,状态机用箭头表示流转方向,比如正常到冻结、冻结到正常、正常到销户。别小看这个状态机,很多生产问题就是状态判断漏了一种情况,导致该拦的交易没拦住。
3.4 总账与内部户:所有分录的最终归宿
总账这块是技术同学最容易轻视、财务同学最关心的部分。核心系统里每一笔联机交易产生的分录,最终都要落到会计科目上,科目再汇总成总账。总账余额和分户账余额之间必须满足勾稽关系,也就是“总分核对”。
内部户在这里扮演关键角色。举个转账的例子:客户A向客户B转账100元,从账务上看是A的存款账户减少100,B的存款账户增加100,一借一贷正好平衡。但如果这笔钱要收1元手续费,且手续费要先挂账后结算,就需要一个“手续费应收”内部户参与,分录变成A账户减101、B账户加100、手续费收入户加1。再比如跨行转账,钱先出账但对方行还没确认,中间要经过“待清算”过渡户,这时候过渡户的余额就是需要重点监控的对象。
画图时,总账域通常放在最下方,用一条粗线把所有业务域的分录汇总上来。内部户单独列一组,用虚线连到各个业务域,表示“哪个业务会用到哪个内部户”。我强烈建议在图上标注每个内部户的用途和余额方向,因为内部户挂错方向、用错科目,是总分不平的最常见原因之一。
4. 一笔转账的全链路:图上那条线到底怎么走
4.1 联机交易的九个步骤,每一步都有卡点
把一笔跨行转账拆开看,核心系统内部大致要走九步:报文接收与格式校验、幂等检查、账户信息查询、交易准入校验、风控与限额检查、账务分录生成、余额更新与流水登记、记账结果返回、异步通知与登记。这九步在图上应该画成一条带序号的线,每一步旁边标注关键校验点。
幂等检查这一步值得单独说。渠道重复发报、网络超时重试、批量重跑,都会导致同一笔交易进来多次。核心系统的做法是要求每笔交易带一个全局唯一流水号,账务层对流水号建唯一索引,重复请求直接返回首次结果,不重复记账。这个设计在图上就是一个“漏斗”形状的检查点,所有交易必须先过这一关。
账务分录生成这一步是业务逻辑的核心。以扣款转出为例,分录通常是:借客户存款账户,贷待清算过渡户。对方行确认后,再做:借待清算过渡户,贷存放央行款项或者同业存放。这两组分录分属不同时点,图上要用两条线分别表示“发起时”和“清算时”,不能合并。
余额更新这一步必须强调事务边界。账户余额、流水表、分录表三者的更新要在同一个本地事务里完成,任何一步失败都要整体回滚。如果这三个操作跨了服务、跨了库,就必须引入可靠消息或者对账补偿,这时候图上要明确标出“分布式事务处理点”,不能含糊带过。
4.2 借贷平衡怎么落:分录表和余额表是两张皮
很多同学画图时把“记账”画成一个动作,实际上记账至少涉及两张表:分录表和余额表。分录表记录每一笔借贷方向、科目、金额、交易流水号,是流水性质的;余额表记录每个账户、每个科目的当前余额,是状态性质的。两者必须同步更新,且分录表的借贷合计必须相等。
这里有个实操细节:分录表一般不存“余额”字段,只存发生额,余额是靠余额表维护的。日终的时候,会拿当天的分录发生额去和余额表的变动做核对,这就是所谓的“发生额核对”。如果某笔交易只更新了分录没更新余额,或者反过来,核对就会报错。所以画图时,我建议在账务层画两个并排的方框,一个写“分录(发生额)”,一个写“余额(状态)”,中间用双向箭头连起来,表示必须成对更新。
借贷平衡的校验点也要标出来。联机交易在提交前要做一次借贷合计校验,不等就不许提交;日终批量再做一次全量校验,把当天所有分录按科目汇总,检查借贷是否相等。这两道关卡是账务准确性的底线,图上不画,等于告诉后来的人“这里可以省”,迟早出事。
4.3 幂等、冲正、对账:一致性三件套缺一不可
幂等解决的是“重复请求”,冲正解决的是“记错了要撤回”,对账解决的是“两边数据不一致怎么发现”。这三件事在图上应该作为账务层的三个独立能力画出来,而不是散落在各个业务流程里。
冲正的处理方式有两种:红字冲正和蓝字反向。红字冲正是用负数金额记一笔反向分录,原分录保留;蓝字反向是记一笔方向相反的正数分录。两种方式都能把余额改回来,区别在于对流水和报表的影响。我个人更倾向红字冲正,因为原交易痕迹完整保留,审计追溯时一目了然。冲正必须带授权,大额冲正还要走复核,这个控制点在图上用一个小锁的图标位置标注一下,提醒后来者别忘了。
对账分三层:渠道对账、总分核对、内外对账。渠道对账是核心系统和渠道系统之间的流水比对,总分核对是分户账和总账之间的余额比对,内外对账是和清算机构、第三方支付之间的资金比对。三层对账的频次和口径不同,图上要分别画出来。对账不是为了好看,它是发现不一致的最后一道网,网眼大小取决于业务的重要程度,这个道理在图上标清楚,比写十页制度都有用。
5. 利息怎么算:把公式拆到能敲计算器验证
5.1 活期计息的日积数法,一步步算给你看
活期存款利息的计算,业内主流做法是日积数法。核心公式是:利息等于累计计息积数乘以日利率,日利率等于年利率除以计息基准天数。计息基准通常取360或者365,这个参数在产品里配置,不同产品可以不一样。
举个具体例子。某活期账户年利率0.3%,计息基准360天,账户余额在30天内一直是10000元,没有变动。日利率等于0.3%除以360,等于0.000008333。累计积数等于10000乘以30,等于300000。利息等于300000乘以0.000008333,约等于2.5元。这个数你可以拿计算器直接验证,如果系统算出来是2.53或者2.47,就要去查计息基准和天数口径。
实际系统里余额每天都可能变,所以积数是按日累加的:每天日终取账户余额乘以当天的天数权重,累加到一个积数余额字段上,结息日再用积数乘以日利率。这里有两个容易踩的坑:一是起息日当天的余额算不算积数,不同产品规则不同,参数里要配清楚;二是结息日当天发生的交易算不算当期,通常以日切时点为准,日切之后发生的交易计入下一期。
画图时,计息模块要画出三个输入:账户余额变动流水、利率参数、计息基准参数;一个输出:积数余额和应收应付利息。积数余额这个中间字段很多人不画,但它是排查利息差异的关键抓手,建议在图上明确标出来。
5.2 定期与贷款计提:时点不同,算法不同
定期存款和贷款的利息处理逻辑跟活期不一样,它们是“计提制”。定期存款在存期内按月或者按季计提应付利息,到期一次性支付;贷款则是按月计提应收利息,同时按五级分类计提减值准备。计提的意义在于权责发生制,收入和成本要落在对应的会计期间,不能等到实际收付才入账。
以一笔100万元、年利率4.35%、期限一年的贷款为例。按月计提,月利率等于4.35%除以12,等于0.3625%,每月计提利息3625元。分录是借应收利息,贷利息收入。客户实际还息时,再借客户存款账户,贷应收利息。如果客户逾期没还,应收利息要转入表外科目,同时计提减值准备,这时候涉及的科目就更多了。
计提这块在图上要画出“计提引擎”这个模块,输入是贷款账户的余额、利率、期限、还款方式,输出是计提金额和分录。旁边要标注计提频率和计提日,通常是每月末或者每季末。计提金额算错,直接影响利润表,所以这个模块的测试用例要覆盖提前还款、逾期、利率调整、期限变更这些边界场景。
5.3 日终批量:顺序错了,账就平不了
日终批量是核心系统每天最重的一段活,顺序安排非常讲究。我一般的建议顺序是:日切(确认当日交易截止)、联机交易补记、计息积数累计、结息处理、贷款计提、手续费摊销、总分核对、报表生成、数据抽取、日启(切换营业日期)。
这个顺序里,日切必须在最前面,因为它决定了哪些交易算今天、哪些算明天。计息积数累计要放在结息之前,因为结息要用到当期积数。总分核对要放在所有账务处理之后,否则核对的是一本还没记完的账。报表生成和数据抽取放最后,它们只读不写,不影响账务。
日终超时是生产上最常见的批量问题。原因通常有三类:单笔交易处理慢导致批量积压、数据库批量更新锁等待、批量任务之间依赖顺序设计不合理。排查的时候先看批量监控里哪个任务耗时异常,再看那个任务的SQL执行计划,最后看是不是有长事务没提交。图上把批量任务画成一条带时间轴的流水线,每个任务标注预计耗时和依赖关系,运维同学排查起来会顺手很多。
提示:日终批量一定要做断点续跑和重跑幂等。跑到一半失败,不能从头再来,否则已经计提的利息会重复计提。每个批量任务要有独立的运行状态表和幂等键,这个设计在图上用一个小的“状态表”方框标出来就行,但千万别省。
6. 从集中式到分布式:改造时图要怎么改
6.1 拆分边界:按业务域拆,不按表拆
核心系统做分布式改造,第一个难题是拆分边界。我的经验是按业务域拆,不按表拆。存款、贷款、支付结算、总账、客户,这五个域各自有独立的生命周期和事务边界,拆开之后跨域调用相对少。如果按表拆,比如把账户表放一个服务、流水表放一个服务,那每笔交易都要跨服务,事务成本高得离谱。
拆分的具体做法是先画领域模型,找出聚合根。存款域的聚合根是账户,贷款域的聚合根是借据,支付域的聚合根是支付指令,总账域的聚合根是科目余额。一个聚合内的操作保持本地事务,跨聚合的操作走最终一致。图上用不同的颜色块表示不同域,域内用实线,域间用虚线加箭头,标注“异步”或者“补偿”。
这里有个现实约束:老核心系统的表结构往往是几十个业务共用一套账户表,想拆干净需要先做数据解耦。比较稳妥的路径是先做逻辑隔离,把不同业务的账户打上业务标识,再逐步迁移到独立库。这个过程在图上表现为“过渡态”,画图的时候要专门画一张演进路线图,把目标态和过渡态分开,否则实施团队会拿着目标架构图去改现有系统,改到一半发现根本落不了地。
6.2 热点账户:拆、缓、限,三招够用
热点账户是分布式核心绕不过去的坎。一个代发工资账户、一个平台商户的收款账户、一个手续费收入内部户,短时间内可能有成千上万笔交易打上来,行锁竞争直接把吞吐量压下去。
常用的解法有三招。第一招是拆,把热点账户拆成若干子账户,交易按客户号或者流水号哈希路由到子账户,查询时再汇总。第二招是缓,把高频小额扣减放到内存缓冲,累积到一定笔数或者一定时间再批量落库,代价是实时余额不精确,需要配合可用余额的预扣机制。第三招是限,对单账户设置交易频率上限,超过就排队或者拒绝,用业务手段削峰。
这三招在图上分别对应三种不同的画法:拆是横向复制多个账户方框,缓是加一个缓冲队列,限是加一个限流阀门。实际项目里通常是组合使用,比如代发工资用拆加缓,内部户用缓加限。选型的时候要看业务对实时余额的要求,要求强实时的只能拆,能容忍秒级延迟的可以缓。
6.3 数据库选型与迁移双跑:别拿生产当试验田
数据库选型这块,我的观点比较务实:没有银弹,只有取舍。集中式商用库的优势是事务强、生态成熟、运维工具全,劣势是扩容成本高、单点风险;开源分布式库的优势是水平扩展、成本低,劣势是跨分片事务弱、复杂查询性能不稳定、运维门槛高。
核心系统的账务库,如果交易量在可控范围内,我倾向于保留强事务能力的方案,把扩展性压力往应用层和缓存层转移。如果确实要上分布式库,必须先把分片键设计清楚,账户号或者客户号做分片键是常见选择,但要注意跨分片查询和全局唯一序列号的实现。
迁移是风险最高的环节,标准做法是新老双跑。具体步骤是:先做静态数据迁移(客户、账户、参数),再做增量数据同步(流水、余额),然后让新系统以只读方式接查询流量做比对,比对一致后再切部分写流量,最后全量切换。双跑期间要有自动比对工具,逐账户比对余额、逐笔比对流水,差异要能定位到具体字段。图上把双跑画成两条并行的流水线,中间用比对工具连接,切流开关单独标注,回退路径用反向箭头画出来。回退方案不画,评审过不了,这是硬规矩。
7. 问题排查实录:那些图上看不出来、只有踩过才知道的坑
7.1 账务不平的排查路径:从科目倒推账户
账务不平是核心系统最典型的问题,表现是总分核对差异、借贷不平、或者余额与流水不符。排查我一般按四步走:先定位差异科目,再拉该科目当天的全部分录,按交易流水号分组核对借贷,然后拿异常流水号去查联机日志,最后看是不是有内部户挂错方向。
常见原因有五种:一是过渡户余额未及时清理,比如跨行转账对方行一直没确认,钱挂在待清算户上;二是冲正交易只冲了余额没冲分录,或者反过来;三是批量计提重复执行,导致费用科目多计;四是并发更新余额时丢更新,账户余额比流水累计少;五是参数配错,比如新产品的科目映射指到了错误的内部户。
这里面最隐蔽的是丢更新,因为它不会报错,账面上看余额是正的,只是比实际少了。排查方法是拿账户余额和当天所有流水按时间顺序累加一遍,对不上就说明中间有更新丢失。根治办法是余额更新加乐观锁或者悲观锁,同时在分录表上记录更新前的余额和更新后的余额,方便事后追溯。这个字段设计在图上就是一个不起眼的小格子,但它是排查利器。
7.2 日终超时与批量优化:先看锁,再看SQL,最后看架构
日终批量超时,我的排查顺序是:先看数据库有没有锁等待,再看耗时最长的几条SQL执行计划,最后才考虑是不是要拆分批量任务。大部分情况下问题出在前两步。
锁等待的典型场景是批量更新账户表时,联机交易还在跑,两边抢同一批行锁。解决办法是日切后先停联机写交易,或者把批量更新改成分批提交,每批几千条,减少锁持有时间。SQL问题的典型场景是全表扫描、索引失效、统计信息过期,这些用执行计划一看便知。架构层面的问题通常是批量任务串行依赖太多,能并行的没并行,这时候才需要动刀子重构。
还有一个容易被忽略的点:批量任务的日志。很多团队的批量日志只记录“开始”和“结束”,中间没有任何进度信息,一出问题只能干等。我建议每个批量任务至少记录已处理笔数、当前处理到的主键、耗时,这样断点续跑和问题定位都有依据。图上画批量流水线的时候,把“进度日志”作为每个任务的标配画进去,后期运维会感谢你的。
7.3 常见问题速查表
| 问题现象 | 优先排查方向 | 常见根因 | 处理建议 |
|---|---|---|---|
| 总分核对有差异 | 差异科目当天的分录 | 过渡户未清理、冲正不完整 | 逐笔核对分录,补记或冲销 |
| 账户余额与流水累计不符 | 并发更新、批量重复执行 | 丢更新、计提重跑 | 加锁、批量幂等键 |
| 利息金额与预期有尾差 | 计息基准、天数口径 | 360/365配置错误、起息日算错 | 核对产品参数和日切时点 |
| 联机交易超时 | 数据库锁、慢SQL、下游依赖 | 热点账户行锁、批量占锁 | 拆子账户、错峰批量 |
| 重复记账 | 幂等键缺失或重复 | 渠道重发、批量重跑 | 流水号唯一索引、结果缓存 |
| 迁移后余额有分差 | 精度截断、币种舍入 | 金额字段精度不一致 | 统一精度规则,逐户比对 |
| 产品开通后交易报错 | 产品参数、科目映射 | 参数漏配、科目未授权 | 建立产品上线检查清单 |
| 日终任务卡住不动 | 锁等待、死循环、依赖未完成 | 长事务、前置任务失败 | 查锁、查依赖、看进度日志 |
这张表我建议直接贴在项目组的墙上,新同学入职第一周就让他背下来。不是因为这些问题多高深,而是因为它们出现频率太高,有一个固定路径能省下大量沟通成本。
7.4 几个只有踩过才知道的经验
第一个经验:新产品的参数配置一定要做“配置即测试”。产品工厂给了业务很大的自由度,但自由度越大越容易配错。我的做法是每配一个新产品,就用一组标准交易跑一遍,开户、存款、取款、结息、销户,把结果和手工计算比对,比对通过才允许上线。这个动作看着笨,但比上线后改数据便宜太多。
第二个经验:账务层的任何改动都要有“双人复核”和“可回退脚本”。改余额、改状态、改分录,这三类操作必须有审批和回滚方案。我见过一次生产事故,运维同学手工改了一个账户的冻结金额,忘了同步改可用余额,结果客户第二天取不出钱,排查了整整一天。
第三个经验:画图的时候给“异常流”留位置。大部分架构图画的全是正常路径,一笔交易顺顺利利走完,但真正消耗人力的是异常:超时怎么办、对方行拒绝怎么办、记账成功但通知失败怎么办、批量跑到一半宕机怎么办。把这些异常分支用虚线画在图上,标注处理策略,这张图才具备实战价值。
第四个经验:不要迷信“一图搞定”。一张图能讲清楚主干,但讲不清楚细节。我的习惯是一张总览图加若干张局部图,总览图讲清分层和流向,局部图讲清关键模块的内部逻辑,比如计息、分录生成、批量调度。图与图之间用编号关联,形成一套图册。这样既保证了整体感,又不至于一张图上挤满几百个方框,谁看谁晕。
最后分享一个我个人画图的习惯:每次画完,我会拿一笔真实业务从头到尾“走”一遍图,边走边问自己“这一步的数据从哪来、到哪去、失败了怎么办”。走不通的地方就是图上的漏洞,补上再走一遍。这个笨办法帮我避开了很多次评审时的尴尬,也让图真正变成了团队共用的语言,而不是挂在墙上的装饰。