01

什么是账户

我们先看看标准定义:账户是根据司帐科目设置的,具有一定格式和构造,用于反响司帐要素的增减变动情形及其结果的载体。

php余额冻结设计资金账户体系的设计 Node.js

从业务视角来看账户实在便是用于记录某个主体的某类型资金的余额以及余额变动明细的数据载体。

如果从支付这块业务来看的话,账户是支付机构内部为其做事工具(用户、商户、银行等)创建的物理记录,这些记录包含了工具的关键信息,如机构为工具分配的唯一 ID、工具的余额、交易的流水、账户状态等等。
可以说账户是支付机构识别做事工具的根本,所有做事工具都必须要有账户。

综上所述,账户有3个关键的点:

账户余额:账户中的资金数目,这个账户有多少钱;账户流水:这个账户资金进进出出的明细记录,任何余额的变动都须要记录流水;交易凭据:用来记录交易过程的信息。

三者紧张内容包括:

1.1 余额

账户余额记录用户的资金数目,当发生交易时,会对余额进行更新操作。
一样平常余额信息包括以下字段:

账户 ID,这个 ID 有的平台会有编码规则,比如某些位用来区分个人或者商户,区分币种以及校验位等。
币种。
余额,一样平常利用币种最小单位。
账户状态,比如是正常态、支付或者冻结等。
余额版本号,这个字段非常主要,表示了余额的变革过程,是与流水进行关联的关键。

1.2 流水

账户体系的目的是记账,仿佛只记录余额,对余额进行增编削查就可以了。
但是这样做有两个弊端:1.没有办法表示出余额变革的过程。
2.账户余额被修改了没有办法追查。
以是在记录余额的同时,还须要记录每一笔变革的过程,也便是账户的流水。

当余额发生变更时,须要同步记录流水,以此来跟踪余额的变革,流水信息一样平常包括如下内容:

流水 ID。
凭据 ID,一样平常是业务单号(如订单 ID、退款单 ID 等),也便是下面凭据中的 ID。
发生金额。
发生币种。
起始余额。
终止余额。
余额版本号,与余额信息中的版本号字段对应。
资金方向,标识是入账还是出账。
源账户 ID。
目的账户 ID。
交易韶光戳。

总体来看,流水和余额互为冗余数据,流水不仅可以有效地减少由于内部缺点导致的账户余额缺点的问题,也便于账户系统与其他外部系统进行对账,以是账户系统记录流水是非常必要的。

在设计账户流水时,有几个主要的原则必须遵守:

流水记录只能新增,一旦记录成功不许可修正和删除。
纵然是由于正当缘故原由须要取消一笔已经完成的交易,也不应该去删除交易流水。
精确的做法是再记录一笔“取消交易”的流水。
流水中的余额版本号必须是连续递增的,我们须要用余额版本号来确定交易的先后顺序(把稳:不是通过交易韶光戳)。

1.3 凭据

凭据用来记录交易过程中的信息,是用户交易的依据。
凭据对应到支付平台内部的各种单类,比如充值单、提现单、交易单等等。
一样平常包括:凭据 ID、交易参与方、交易金额、交易类型、交易状态(比如支付中,支付成功,转退款等)、交易渠道等信息。

凭据单理论上可以放到业务层,不放在账户核心层,这样账户层就只有余额和流水。
捉住了余额、流水这两个点我们基本就捉住了资金账户系统设计的核心了。

02

什么是资金账户系统

资金账户系统,大略的类比便是一个存折账本,即用户的每一笔资金变动全部在账本上反响出来。
对付银行给我们开立的账户,称为银行账户;支付公司给我们开立的账户称为支付账户;电商平台给用户开立的账户称为电商虚拟账户;由于它不像银行一样真实地记录资金变动情形,只是将账记好而已,以是它是虚拟的而不是实体账户。

常见场景:微信的零钱账户、支付公司的商家账户、电商平台的推广佣金账户、余额账户等均属于虚拟账户。

第三方支付作为中立的第三方,截断了用户和商户的资金流,资金先从用户账户转移到第三方支付平台账户,得到双方确认后再从支付平台账户转移到商户账户。

支付平台为客户供应了资金流转以及结算等做事,必须建立自己独立的资金账户系统,以此来担保每个客户资金的准确性以及资金变动的可追溯性,这套资金账户系统在支付平台中成为“核心”,整体类似于银行的账户核心,但会比银行账户核心大略一些。

2.1 资金账户系统的目的

账户的核心目的,便是将账记清楚,不能涌现错记、漏记。
同时在实际业务中,账户体系也可以办理分外情形的业务,由于并不是所有的业务变动都会在业务中记录,但是却须要在账户系统中表示。

2.2 资金账户系统的构成

一个账户体系一样平常分为账户构造和账务构造两部分;账户构造用于记录一个账户基本信息、类型、当前余额等;而账务构造则是用于记录每个业务对应的余额变动情形。
这里好理解,说白了便是第一节中说的余额和流水在系统中各自对应的实体。

03

实现一个支付资金账户系统

3.1 主要原则

由于资金账户系统本身属于司帐范畴,故一定是须要遵守一系列的司帐准则的。
比如:内部账户记账的一条原则:有出必有入,出入必相等。
所有的记账操作都必须遵照这一条原则,这是为了确保交易安全,避免凭空加钱和减钱行为的涌现。
同时,该原则也是各种核对(核心事务核对、备付金核对等)系统的依据之一。

账户记账的理论根本基本都是基于借贷记账法的,什么是借贷记账法呢?

3.1.1 复式记账法

复式记账法的英文为 Double Entry Bookkeeping,是从单式记账法发展起来的一种比较完善的记账方法。
也叫复式记账凭据。
与单式记账法比较较,其紧张特点是:对每项经济业务都以相等的金额在两个或两个以上的相互联系的账户中进行记录(即作双重记录,这也是这一记账法被称为“复式”的由来);各账户之间客不雅观上存在对应关系,对账户记录的结果可以进行试算平衡。
复式记账法较好地表示了资金运动的内在规律,能够全面地、系统地反响资金增减变动的来龙去脉及经营成果,并有助于检讨账户处理和担保账簿记录结果的精确性。
在我国,复式记账曾有借贷记账法、增减记账法、收付记账法三种,但规定利用的只有借贷记账法一种。

3.1.2 借贷记账法

借贷记账法的记账规则可以概括为:有借必有贷,借贷必相等。

第一,在利用借贷记账法记账时,对每项经济业务,既要记录一个(或几个)账户的借方,又一定要记录另一个(或几个)账户的贷方,即“有借必有贷”;账户借方记录的金额一定即是账户贷方的金额,即“借贷必相等”。

第二,所记录的账户可以是同类账户,也可以是不同类账户,但必须是两个记账方向,既不能都记入借方,也不能都记入贷方;

第三,记入借方的金额必须即是记入贷方的金额。

司帐恒等式:资产=负债+所有者权柄:“借”对应资产的增加、负债及所有者权柄的减少。
“贷”对应资产的减少、负债及所有者权柄的增加。

这些观点由于涉及到太多的司帐知识,比较抽象,要理解还须要费点功夫。
我说一下我的大略理解。

对付第三方支付业务来说,由于银行账户对应着备付金,故是属于资产。
而商户和用户的账户属于支付平台内部账户,属于负债。

每一笔资金流操作该当都是“成对”的,至少会在两个干系账户中记录金额相等、借贷相反的流水。
理论上不应该有一笔单独存在的流水。
两个账户的类型可以是相同的(比如都是负债类账户),也可以是不同的。
比如转账,A 的账户有出账的流水,则一定 B 的账户有一笔与之对应的入账流水,这里两个账户都是负债类账户。
那有人可能会问,那么充值、退款、提现的流水呢,他们也会有两笔流水吗?答案是肯定的。
它会对应着一笔负债类账户的流水和一笔资产类账户(银行账户)的流水,且两者借贷方向同等(同时加钱或者同时减钱)。

3.1.3 账户的基本操作

一样平常的账户系统都会供应开户、查询余额、充值、扣款、转账等基本接口。

3.2 我们的设计与实现

讲了这么多理论和事理,该进入正题了。
我们在项目中是如何实现一个资金账户系统的呢?

由于项目哀求的分外性,我们的资金账户系统仅须要记录商户干系的账户,而不须要记录用户干系的账户。

3.2.1 资金流设计

首先是资金流设计,个中也包含了账户类型的设计。
这里的原则是实事求是,统统从实际出发;如无必要,勿增实体。
比如是否须要保留待结算 B 账户的问题,我们团队也是经由一番思考。
终极考虑到产品规则设置我们的结算规则并非异步结算,而是单笔实时结算,故而我们取消了待结算 B 账户。
终极整体账户类型和资金流设计如下。

关于手续费账户,可以分开收入和支出两个账户,更为安全,可用性也会更高。
由于这样永久只有一个方向的资金流动。
当然如果没那么高的性能哀求,也可以合为一个统一账户。
我们这里基于项目的量级决定简化设计,采取了后者的办法。

关于银行账户,它是一个虚拟账户,其可实时/动态反应每笔钱款变动,特殊是实时记录在途的银行资金。
为什么要引入银行账户,第一是由于要符合3.2的原则,避免单边账导致的借贷不相等的问题;第二是由于要确保系统虚拟资金流和银行物理资金流对齐,避免资金是非款。

本系统中结算、退款、提现、手续费充值等涉及外系统资金流向的情形,会记录银行账户流水。
记录流水的办法有同步和异步两种,常日对付银行流水的记账采取异步的办法即可,我们采取的是一个 Daemon 程序异步导入银行账户流水。

3.2.2 存储选型

如果说全体系统是一座大厦的话,那存储便是地基。
地基不牢,地动山摇。
资金账户系统对付存储的哀求是比一样平常的系统更高的。
详细有哪些哀求呢?

高性能。
这样才能承受高并发的要求流量。
数据强同等。
担保切换不同副本的情形下数据0丢失0出错。
支持透明分库分表。
在分布式形态下,用户看到的逻辑表的实际物理存储可能是被打散分布到不同的物理节点上。
希望做到对业务完备透明。
这样一来作为开拓者不须要关心分库分表细节。
支持一直机的容量弹性扩展。
如果性能或容量不敷以支撑业务发展时会自动扩展,升级过程中,开拓者无需关心分布式系统内的数据迁移,均衡和路由切换。
支持跨区容灾、同城双活、跨城容灾等,故障自动切换其他副本,自动规复,确保99.999%的可用性。
最好有配套的分布式事务办理方案。

除此之外还有其他一些哀求,这里就不赘述了。
当然这些特性全部具备那是最空想的情形。
接下来我们看下业界有哪些实现方案。

基于 MySQL 的方案。
基于 NoSQL 型分布式 KV 存储的方案。
基于 TDSQL 的方案。

TDSQL 是腾讯打造的一款分布式数据库产品,具备强同等高可用、环球支配架构、分布式水平扩展、高性能、企业级安全等特性,同时供应智能 DBA、自动化运营、监控告警等配套举动步伐,为客户供应完全的分布式数据库办理方案。
其支持 MySQL 的大部分语法,且由于其分布式的架构,可以完美地支持以上提到的所有技能哀求。
同时其面向金融级的产品特性已在诸如微众银行等各大金融机构的线上做事中得到验证,因此我们终极选择了基于 TDSQL 来构建我们的资金账户系统。

终极我们选择基于 TDSQL 来作为我们的底层存储,并利用基于账户 ID 作为 sharding key 的自动水平拆分策略。

3.2.3 架构设计

至于架构设计,便是比较迎刃而解的事了,全体资金账户由资金做事和账户做事两个核心做事、两个 DB 和多少 Daemon 程序组成。
资金做事属于资金领域,感知上层业务逻辑;账户做事属于账户领域,不感知上层业务逻辑。
这样做的好处是变动相对频繁的资金领域和变动不频繁的账户领域进行了隔离,上层业务也不须要与账户做事直接打交道,担保了账户做事的稳定性。
知足高内聚、低耦合的哀求和开闭原则。

3.3 重难点

说到全体系统的重难点,实在就两个。
一个是资金安全,一个是高可用性。
而我们的目标,担保资金的绝对安全和尽可能高的可用性。

3.4 资金安全培植

看了以上的内容,大家会知道说白了资金账户便是数据库里的一行记录,账户余额便是一个字段。
因此不当操作极易引起安全事件。
资金安全,“国之大事,去世生之地,存亡之道,不可不察也”。
资金账户系统的最主要的哀求便是资金安全,而资金安全的核心便是一句话:帐记清楚,不出错。

资金安全从源头戒备上可以又分为两个方面:1.戒备外部攻击威胁风险。
2.防止系统内部缺点。

终极目标是担保全体系统符合合法性、完全性、精确性三大哀求。

3.4.1 STRIDE 模型

首先我们看外部攻击威胁风险。
光靠凭空想,是随意马虎产生遗漏的。
那么这里是否有理论支撑呢?答案是肯定的。

功能安全风险剖析是对系统的系统性失落效和随机性失落效进行风险评估,对付网络安全风险,须要通过威胁剖析识别系统的威胁场景,用于形成有对应威胁的掌握方法和有效的分层防御,威胁剖析是信息安全风险剖析的主要组成部分。

威胁识别有不同的办法,如 STRIDE 模型、攻击树、Kill Chain 等。
STRIDE 在软件安全剖析领域运用较多,是微软开拓的用于威胁识别的工具,它把威胁分成如下6个维度来稽核:

Spoofing(仿冒)Tampering(修改)Repudiation(抵赖)Information disclosure(信息透露)Denial of Service(谢绝做事)Elevation of Privilege(特权提升)

微软将 STRIDE 运用于软件的威胁识别,这6个维度也可以扩展到系统层面。

我们也可以利用 STRIDE 来对我们的系统安全性进行剖析。

3.4.2 权限掌握

权限校验担保只有合法的资金操作要求会得到处理,它是资金安全的第一道防线。
权限校验包括数据库、机器的权限校验、业务层的权限校验。
紧张目的是办理 STRIDE 模型中的仿冒、特权提升、抵赖等安全威胁。

数据库权限校验哀求每个 DB 库都只许可本做事访问,不许可跨做事调用,且数据库密码要托管至开拓职员无法获取的地方。

机器权限校验的紧张方法是采取腾讯云 TKE 的内网隔离策略,同时限定开拓职员登录现网机器实现的。

业务层的权限校验紧张包含两个紧张方法:票据校验和模块认证。

票据校验:

首先看理论根本。
访问掌握是指防止对任何资源进行未授权的访问,从而使打算机系统在合法的范围内利用,实质上是一种授权机制。
访问掌握有很多种模型,最常见的三种模型分别是自主式访问掌握(DAC)、逼迫访问掌握(MAC)和基于角色的访问掌握(RBAC):

自主式访问掌握:主体对其拥有的数据具有绝对掌握权,也可以授权别人访问和操作。
适用于大略的业务场景。
逼迫访问掌握:系统将对数据的操作分为不同的级别,逼迫哀求对不同级别数据的访问和操作具有不同级别的鉴权。
适用于对数据安全有极高哀求的系统。
基于角色的访问掌握:抽象出不同角色,授予不同角色下用户以不同权限。
适用于权限划分繁芜的业务场景。

要想做支付业务,须要达到国家等保三级认证,认证哀求系统必须对所有的主体和客体采纳逼迫访问掌握。

票据校验即是一种逼迫访问掌握的办法。
“票据”代表本次要求的身份。
天生票据的条件是证明了身份。

举两个例子:

商户向商户 API 发起要求时须要带 API 密钥署名或证书署名,我们的 API 做事会验证这个署名,验证通过才会实行后面的业务逻辑。
商户在商户平台登录后,每次要求前会自动通过 PHP 的 hook 验证登录态,验证成功才会实行详细的业务逻辑。

在这些例子中,验证署名、登录态或其他能够证明身份的信息便是鉴权的过程,验证成功就代表我们相信本次要求真的是由商户发起的,即证明了商户的身份。

验证成功后,我们就用商户身份等关键信息,加上一个我们自己天生的署名(用于防假造),天生一个字符串,称为票据。
这个过程称为颁票。

票据的天生有鉴权的担保,因此票据本身可以用作鉴权。
票据天生后随着要求向后通报,每个收到要乞降票据的做事都可以通过验证票据署名合法性和身份同等性来完成鉴权。
这个过程称为验票。

通过颁票和验票,我们可以将全体微做事系统边界繁芜而多样的鉴权手段收归为一种统一的鉴权办法:

接入层做事鉴权颁票,其他做事验票。

模块认证:

我们的做事支配在腾讯云 TKE 上,大部分以 RPC 接口的形式供应。
只要知道供应 Server 的 IP 和 Port,那大部分的接口都可以直接调用,如果有坏人进入了 TKE 内网那这基本便是不设防,短板非常明显。
而对付受保护的接口,一样平常是基于票据/IP 限定等手段保护,而这些保护手段也面临着各种问题。
如商户票据并不能识别调用源,IP 限定在稠浊支配情形下可能误限定,运维本钱高且安全性一样平常。
以是这个时候一样平常都须要做一个高下游做事认证。
高下游做事认证是指每个做事要限定调用自己的来源做事,不管这个做事支配在什么机器上。
并且任何做事或者工具也无法伪装成别的做事来访问下贱做事。

3.4.3 防修改

防修改是指防止通过直接修正数据库字段等办法直接修正账户和流水等数据。
紧张目的是办理 STRIDE 模型中的修改的安全威胁。
这一点一样平常是通过打算主要字段的 MAC (全称 Message Authentication Code)值,也便是择要值,并保存到数据库中的办法来实现的。

详细实现一样平常是通过hmac算法打算关键字段的 hash 值来实现的。
hmac 是 Hash-based Message Authentication Code 的简写,便是指哈希认证码,包含有很多种哈希加密算法,sha256 是个中一种。

对付账户系统而言,最主要确当属于账户表的数据,须要在插入和更新时对其主要字段合并起来利用 hmac-sha256 进行 MAC 值打算,并将打算结果作为一个字段(如data_mac,为担保对新老数据的兼容,常日还有一个 data_mac_version 字段表示其版本号)存储在账户表中。
而在查询时增加校验 MAC 值的校验,如果校验失落败可以采纳告警等手段进行主动创造。

3.4.4 密钥防泄露

可以看到,我们之前的诸多方案都会涉及一些敏感数据,如署名用的私钥、数据库的账号密码等。
如果这些信息透露了,那么之前所描述的方案也就不屈安了。
以是本节的目的紧张是防止发生 STRIDE 模型中提到的信息透露问题。

业界常日实践是利用密钥管理系统(KMS)来对密钥进行统一管理。
密钥管理系统是一款安全管理类做事,可以让利用者轻松创建和管理密钥,保护密钥的保密性、完全性和可用性,知足用户多运用多业务的密钥管理需求,符合安全的哀求。

我们先来看看业界最佳实践。
以 Google Drive 为例,所利用文件加解密方案如下:

个中图中的密钥管理模块便是 KMS。

除了密钥的安全保存外,KMS 还有一个优点,可以实现密钥的自动轮换。
由于利用软件产生的伪随机数的安全度要低于专用硬件加密机天生的真随机数,KMS 利用了基于硬件加密的真随机数,这大大提高了密钥的强度和随机性。

KMS 是云平台和产品合规的根本安全组件,外洋用户须要知足 FIPS-140-2标准,海内用户须要支持国密。
一样平常的密钥管理系统供应以下特性:

基于硬件加密机的真随机数。
密钥的权限细粒度管控。
密钥的自动轮换。
密钥生命周期的管理(创建、开启、禁用、操持删除、销毁等)。
自有密钥的导入。
多级密钥管理。

腾讯云已上线知足海内国际合规需求的 KMS,利用指引可以参考:《T-Sec 密钥管理系统》

3.4.5 密钥防泄露

限流限频相信大家都很熟习,目的紧张是防止发生 STRIDE 模型中提到的谢绝做事问题。
一样平常都是通过引入单机限流或者分布式限流组件来实现的。

3.4.6 其余一个角度

通过以上剖析,STRIDE 模型中提到的6大威胁都得到了相应的办理。
但这仅仅是从戒备外部安全威胁的角度来剖析的,真实系统中资金安全问题,每每是来自于系统内部缺点,所谓“堡垒是从内部攻破的”。
那如何应对可能存在的系统内部缺点呢?从方法论上每每都是通过对全体过程进行事前事中事后的剖析来进行全方位的 review。
这里由于篇幅所限,不侧重 review 的过程,直接给出结论。

3.4.7 幂等性设计

幂等性:便是用户对付同一操作发起的一次要求或者多次要求的结果是同等的,不会由于多次点击而产生了副浸染。

对付账户和交易类系统,做好接口的幂等性至关主要,既是担保资金安全的利器,也是同等性补偿的必要条件。
大略来说,幂等性便是要担保同一个单号有且仅能有一次操作成功。
比如利用单号 deal_no1 做一笔5块钱的充值,假设充值成功后重复利用该单号做充值,程序能够判断该单号已充值过,可以返回调用方已充值成功的缺点码,或返回成功处理(这里采取哪种办法,取决于上层是否须要感知已充值成功这一状态)。
实际无论调用充值接口多少次,统共只有1次充值5块钱成功。

担保幂等性有多少种实现办法。
通用可靠的做法一样平常是通过 DB 对单号做唯一索引,依赖 DB 来防重担保仅能有一笔入账成功。
我的系统中也是采取这一办法,担保资金账户系统所有供应给到上层的接口都是幂等的。

3.4.8 余额流水的同等性

由于资金余额和账户流水是最为主要的两个数据,必须担保同时写入/更新成功,如果涌现不一致会导致严重的资金安全后果,因此须要担保强同等。
此时险些只能利用数据库的单机事务才能担保。
实际上事务这个特性最初便是被设计用来办理交易问题的,在英文中,事务和交易便是同一个单词:Transaction。

确定利用数据库的单机事务实现,又有两种办理方案:

悲观锁方案:创建资金流的时候,每次查询账户时,对该账户加排他锁。
也便是在获取 accountid=1 的账户信息时对该行记录加锁,期间其他要求壅塞等待访问该记录。
悲观锁适宜写入频繁(写多读少)的场景。

乐不雅观锁方案:创建资金流的时候,每次查询账户时,不对该账户加锁,而是获取到账户当前的版本号 version。
在更新数据的时候须要比较程序中的 version 与数据库中的 version 是否相等,如果相等则进行更新,反之程序创建资金流,再次进行比较,直到两个 version 的数值相等才进行数据更新。
乐不雅观锁适宜读取频繁(读多写少)的场景。

在我们的系统中,由于更新账户的频率比查询账户的频率高,基于以上我选择了第一种方案。

3.4.9 分布式事务与终极同等性

类似于转账这样的场景,涉及到两个账户进行操作,之前在3.2.2节提到过我们是基于账户 ID 作为 sharding key 的,那么两个账户可能落在不同的机器上,这里就会涉及分布式事务问题。

分布式事务的办理方案有很多,可以参考《万字长文总结分布式事务-总有一款适宜你》一文。

对付转账这样的场景,业界常日的做法都是基于可靠行列步队、有限状态机与多重任务兜底的业务层终极同等性保障机制来实现的。

也便是把转账分为出账(扣钱)和入账(加钱)。
出账为主事务,必须实时做成功;而入账作为从事务,是可以接管在异步流程做成功的。
那么就利用行列步队驱动入账成功,这中间如果有极少失落败,即利用兜底 Daemon 担保。

我们终极采取的是 TDSQL 原生支持的分布式事务方案。
TDSQL 支持普通分布式事务协议和 XA 分布式事务协议。
TDSQL(内核5.7或以上版本)默认支持分布式事务,且对客户端透明,像利用单机事务一样方便。
TDSQL分布式事务采取两阶段提交算法(2PC)担保事务的原子性(Atomicity)和同等性(Consistency)。
详细实现办法可以拜会《TDSQL MySQL 版 > 开拓指南 > 分布式事务》的官方文档。

至于为什么这么选?紧张的考虑还是你的系统诉求是怎么样的。
根据 CAP 理论,在一个分布式系统中,同等性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)这三个要素最多只能同时实现两点,不可能三者兼顾。
因此你的系统是要选择 CP 呢还是 AP 呢?我们根据我们的业务场景(量级小、异步驱动等特点)选择了 CP,也便是 2PC 的实现办法,当时的一些比拟选型的思考如下所示。

3.4.10 全方位的对账和审计

每个账户系统都不是伶仃存在的,至少要和财务、订单、交易这些系统有着密切的关联。
空想情形下,账户系统内的数据该当是自洽的。
所有用户的账户余额加起来,该当即是这个电商公司在银行专用账户的总余额。
账户系统的数据也该当和其他系统的数据能对的上。
比如说,每个用户的余额该当能和交易系统中充值记录,以及订单系统中的订单对的上。

安全是资金账户系统的生命线。
完全性和精确性靠对账和审计来创造。
它们是资金安全的末了一道防线。

怎么证明账户系统的高同等呢?那就必须通过对账系统来验证,对账韶光粒度越小,就能越早创造问题。

在对账和审计方面,从同等性、精确性、总分核对和及时性四个方面全面梳理并实现系统内和系统间的数据核对和审计,确保有非常发生时能及时创造问题。

同等性:系统间关键数据同等,包含商户、账户、金额、单号、属性、状态等。
精确性:业务规则的精确性,如进出分离实罢手续费。
总分核对:系统内总分数据同等,包含冻结总分(退、结、分)和手续费总分。
及时性:业务单据超期未到终态,如解冻单、分账明细单、退款单。

资金账户的审计对账一样平常包括:

逐层审计对账(由上层驱动)。
流水连续性审计对账。
余额精确性审计对账。
内部银行账户和外部银行流水账单审计对账。

账户流水连续性审计的打算模型:

审计的公式:

单条流水期初余额 + 交易发生金额 = 期末余额。

本条流水期初余额 = 上条流水的期末余额。

本条流水的余额版本号 =上条流水的余额版本号 + 1。

特殊把稳事变:

1)账户流水表须要具有【余额版本号】字段;

2)本账期末了一笔流水留待下个账期再审计。

账户余额精确性审计的打算模型:

审计的公式:

1、当前账户余额=当前余额版本号对应的账户流水.期末余额

2、业务示例:

(A)账户表:当账户余额版本号=3时,账户余额=180

(B)账户流水表:

特殊把稳事变:

1)账户流水表须要具有【余额版本号】字段;

2)先完成账户流水表的连续性审计,再进行账户余额精确性审计。

如果数据不一致,要第一韶光天生业务非常记录并告警。

3.4.11 司帐核算体系

所有上面方法做完,审计对账也都对齐了,就足够安全了吗?非也!
有没有创造上述所有方法都是从研发的角度出发来办理资金安全的问题,只能说从工程系统的角度来看是完备的。
但是从业务的角度来说,资金账户系统涉及公司主要财务数据,是须要财务帮忙审核才行的。
从其余一个角度来看,有了财管的背书,我们系统的安全性也更加得到保障。
因此我们须要建立司帐核算体系。

建立司帐核算体系的目的是担保资金变动可追溯,确保账实符合,及时核算监控资金,避免是非款风险。

需求简述:根据业务流程和特性,设置账户体系和司帐记账科目,实时/T1动态记录各业务场景的司帐账,并输出干系业务总帐报表/司帐调节表等报表,供财务管理账务。

银行存款余额调节表(广义)

广义上的银行存款余额调节表,是指在银行对账单余额与企业账面余额的根本上,各自加上对方已收、本单位未收账项数额,减去对方已付、本单位未付账项数额,以调度双方余额使其同等的一种调节方法。
该表紧张目的是在于核对企业账目与银行账目的差异,也用于检讨企业与银行账目的差错。

系统对账调节表(狭义)

下文简称:调节表,是针对本系统各项业务的资金流转,结合本系统信息流与外部银行资金明细进行对账调节的日常报表。
其紧张浸染是监控本系统资金流向,对业务操作干过后监督。

3.4.12 研发流程规范

这也是一个不可忽略的主要影响成分。
包括以下几点:

严格的现网机器和数据库权限管控。
双人代码 CR。
完全的单元测试和接口测试覆盖。
只能通过发布系统实行发布。

3.5 可用性培植

可用性的定义:可用性便是一个软件系统处于可事情的韶光比例。

打算机系统的可靠性用均匀无端障韶光(MTTF)来度量,也便是说打算机均匀能够正常运行多永劫光,才发生一次故障。
系统的可靠性越高,均匀无端障韶光越长。
可掩护性可以用均匀维修韶光(MTTR)来度量,即系统发生故障后维修和重新规复正常运行均匀花费的韶光。
系统的可掩护性越好,均匀修复韶光越短。

打算机系统的可用性可以定义为:MTTF / (MTTF + MTTR) 100%

可用性可以归结为以下三个问题:程序(节点)的可用性、做事的可用性、存储的可用性。

3.5.1 程序(节点)可用性

实在它和做事可用性严格上说都可以归属于做事的可用性范畴。
这里之以是分开列出来是想突出程序(节点)的可用性更多地是通过资源调度平台的特性来实现的。

腾讯云容器做事(Tencent Kubernetes Engine ,TKE)是基于原生 Kubernetes 供应以容器为核心的、高度可扩展的高性能容器管理做事。
腾讯云容器做事完备兼容原生 Kubernetes API ,扩展了腾讯云的云硬盘、负载均衡等 Kubernetes 插件,为容器化的运用供应高效支配、资源调度。
我们紧张通过 TKE供应的以下特性来担保程序(节点)的可用性。

在私有网络中运行,可以利用自己的安全组和网络 ACL,不与其他业务混布,供应了高隔离水平。
负载均衡,自动分配流量,自动添加和删除容器。
故障自动规复出问题的节点。
自带完备的监控。

其余关于离线周期脚本的调度,则自己搭建和利用了 AirFlow 这一组件,它支持自动重试和缺点关照,担保了离线周期脚本的高可用性。

3.5.2 做事可用性

限流限频

限流限频是可用性担保的兜底方法。
担保 DB 不会被溘然涌入的大量要求压垮,实现的办法便是谢绝超过自身处理能力的要求,对付一个写多读少的系统,这样做是无可厚非的,当然除此以外,我们还要想其他办法提高做事的可用性。

热 key 问题

对付电商秒杀活动,营销发券,企业红包等场景,均有大量用户须要在指定时间点对单一账户做资金转入操作。
由于单个账户已无法再做分库分表处理,单组节点的性能上限就决定了单个账户的交易量上限。
存储层的单账户瓶颈导致在该类场景下业务须要降级或者在业务层实现繁芜逻辑,降落了业务的灵巧性和商户体验。

我们来剖析一下,热点账户账户的核心问题是每笔交易都会对余额字段进行 update 操作,更新时须要对账户进行加锁操作,频繁加锁释锁会对 DB 造成极大的性能压力,可能会超过 DB 的能承载的极限。

办理热点账户问题的常规思路一样平常有以下几种:

只记录流水不记余额。
合并入账。
多账户。

要用更短的韶光装满一个拍浮池无非两种路子,一是让水流速率更快,二是多铺设几条入水管道。
只记录流水和合并入账便是让水流得更快,多账户便是增加多个入水口。

特定运用处景下,可以不记余额只记录流水,这实在违背了记账原则,但在某些场景下,可以通过其他手段来补偿和保障,范例的运用比如 C2B2C 交易,中间的B账户可以不记余额,它的存在只是为了作为中间交易的对手方。
常日对付不须要被直接查询余额的账户,可以考虑是否不记录余额,比如银行账户这种。

合并入账,便是先将余额更新操作 hold 住,暂存入待入账行列步队,累积到一定数量或者到一定韶光时,将发生金额进行汇总合并,更新一次余额,从而大幅提升入账效率。
合并入账借贷先后原则和前面类似,为了戒备风险,只对入账(负债类账户的“贷”)进行合并,出账仍旧须要实时完成。

合并入账只能办理入账时的热点问题,而出账都是须要实时更新(为避免透支风险)。
为理解决出账热点,须要引入多账户体系,即通过新建多个账户,把业务均摊到多个账户上,从而办理热点问题。

多账户不仅可以办理出账热点问题,对入账热点问题也有效,实质上多账户便是把一条 DB 记录的更新均分到了多个 DB 记录上。

按照账户的承载功能不同,多账户方案可以分成两种:

功能分离型多账户,出入帐功能分离,有的子账户承载入帐,有的子账户承载出账,有的承载两者。
功能完全性多账户,所有的分账户都同时承载出账和入帐。

多账户须要保存用户和多个账户的逻辑对应关系,这个对应关系可以放在业务层,也可以放在核心账户层。
比如让商户多申请几个商户号,也是一种选择。

分布式事务带来的性能问题

如3.4.9所提到的,资金账户系统不可避免地会引入分布式事务。
分布式事务会导致可靠性以及性能都会受到影响(由于利用 2PC 等办法)。

这里的思路,一样平常都是通过降落 ACID 中的 C(同等性)和 I(隔离性)而提高 A(可用性)。
详细做法一样平常是在将一个分布式事务拆分成两个本地事务,即“先借后贷”,一个是“借”的实时事务,加一个“贷”的异步事务。
当完成实时事务后就可以对外返回成功,异步事务后续驱动进行。
实质是上通过 MQ 的事务追求达到终极同等性。
但这样做也会引发一些副浸染,在实际利用时须要根据业务场景进行选择。

3.5.3 存储可用性

关于存储的选型已经在3.2.2中讲过了。
这里再强调一次,存储的可用性是全体资金账户系统可用性的根本。

而 TDSQL 作为一个高可用的分布式数据库,对付可用性的上风也表示在以下这些方面:

高性能。
这样才能承受高并发的要求流量。
数据强同等。
担保切换不同副本的情形下数据0丢失0出错。
支持透明分库分表。
在分布式形态下,用户看到的逻辑表的实际物理存储可能是被打散分布到不同的物理节点上。
希望做到对业务完备透明。
这样一来作为开拓者不须要关心分库分表细节。
支持一直机的容量弹性扩展。
如果性能或容量不敷以支撑业务发展时,升级过程中,开拓者无需关心分布式系统内的数据迁移,均衡和路由切换。
支持跨区容灾、同城双活、跨城容灾等,故障自动切换其他副本,自动规复,确保99.999%的可用性。
最好有配套的分布式事务办理方案。

除了上面提到的这些点之外,定期的数据冷备也是必不可少的兜底方法。
同样的,当选用其他存储时,如果以上这些点都能被全面考虑到,那么基本也就差不多了

3.5.4 压测

这里把稳:要把压测当成对设计成果的验收,而不是作为创造问题的兜底手段。
即将上线之前才创造的系统性问题,可能为时已晚。

但笔者还是建议在系统正式上线以前,在测试环境进行至少一次压测,目的有二。
第一是验证可用性指标是否符合预期;第二是通过对付压测数据的监控不雅观察和对账创造资金安全的漏洞,戒备于未然。
经由了压测环节,你的信心也将大大增强。

04

总结

本文以笔者在实际事情中实现的资金账户系统为例,磋商了在资金账户系统设计和实现中会碰着的问题以及相应的办理方案。
可以肯定的是,在资金账户系统培植过程中不会一帆风顺,还会碰着其他各种各样的问题,尤其是一些由于高并发场景引发的问题。
这块笔者也还在持续探索和思考中。
后续大家也可以多多和笔者磋商。