热点账户是在支付转账场景中比较常见的问题,这里把自己理解到和学习到的解决方案分享出来,供大家参考。

1 什么是热点账户

一般热点账户都是银行或支付账户等需要进行代收付等业务的中间内部账账户,普通的客户账户并不会形成热点账户,因为普通客户账户不会存在短时间大量更新余额等情况。

对账户的处理一般包含两部分,一是更新账户余额,二是写账户流水;当有ABCDEF等多个进程同时对账户发起请求,账户在同一时间只能去操作一个进程请求,并对账户进行加锁处理,其他进程必须等待锁释放后再逐个进行更新,所以解决热点户的问题,实际上也可以看成是处理高并发的情景。

热点账户应对方案 - 图1
ABCDEF等多个事务同时对账户发起更新请求:
A事务处理中:1.锁住账户
2.更新账户余额,写账户流水信息
3.提交,释放A对账户的锁
BCDEF等事务处于等待状态,账务处理效率出现瓶颈,等待时间过长交易产生超时

2 热点账户的解决方案及思路

2.1 控制并发数

根据我们前面的描述,最简单的就是控制并发数量,减少同一时间对账户的更新请求,将一些不重要的操作进行分解,减少事务对数据库的访问频率。

这种方式的确可以缓解热点账户的问题,但是此方案的缺点也是不言而喻,账务处理效率明显降低,对于有热点事件发生的场景(如双11抢购)无法满足需求,一旦有大量事务请求,账户处理失败率增加。

2.2汇总明细入账

将多个请求合并为一个请求一次入账

热点账户应对方案 - 图2
新建一个流水登记簿,将每条请求更新记录insert到登记簿中,登记簿中设置一个状态标识,设置为未入账,设定一个定时任务,例如每隔十分钟将所有未入账记录sum为一条记录入账到指定账户。

Insert操作对数据库的开销小,可支持高并发,这样便解决了账务处理瓶颈问题。

但此方案存在一些明显的劣势:结算账户余额没有实时更新,对账户加钱时问题不大,但当对账户减钱时可能会导致账户的透支,存在风险,因此这方案适用于收单类业务,对于存在支出类的业务场景不适用。

2.3缓冲入账

就是将热点账户实时同步的记账请求进行异步化,以达到记账实时性和系统稳定性之间平衡的记账手段,”削峰填谷“。
热点账户应对方案 - 图3
当账户的交易量超过实时处理阈值时,账户先返回成功标识给客户,把待处理的账务请求写入消息队列,待并发量不大时再从消息队列取记录做记账处理。

这种方案实现了准实时的记账,在高并发的情况下客户几乎无延迟感知。但是也存在两个缺陷:一是当账户一直处于高并发时,消息队列不断插入待处理账务,堆积任务越来越多来不及处理,无法达到准实时的效果,客户体验降低。比如A给B转账,A收到转账成功的返回标识,但实际的转账操作并未执行,一直处于消息队列中待处理,B账户实际上一直未收到该笔该笔转账。二是可能出现异常记账的情况:客户发起两笔交易,一笔转账,一笔销户,转账记录记入消息队列中,我们先给客户返回成功,再从消息队列去处理发现实际做转账时客户账已经销户了,转账失败。

2.4 热点账户锁分散

将热点账户分解成多个影子账户(账户1、账户2、账户3…)将发送到账户的多并发进程按照Hash函数平均分配给各个影子账户做账务处理,各个影子账户余额之和等于原热点账户。
热点账户应对方案 - 图4
这种设计思路同样也存在弊端,系统中热点账户本身余额是够做扣款的,但是分成若干个影子账户后,单个影子账户的余额可能就不够做扣款了,所以在实际的设计案例中我们需要考虑优化方案,对扣款交易等一些场景做特殊处理。

2.5 内存数据库

相对于磁盘,内存的数据读写速度要高出几个数量级,将数据保存在内存中相比从磁盘上访问能够极大地提高应用的性能。
可以将账务处理请求实时地更新在内存数据库中,然后异步存储在数据库中。

这种方案对数据库的服务器处理能力要求比较高,内存数据库不能永久保存数据因此存在内存数据库发生异常时数据信息丢失的风险,出于安全性风险系数的考虑,这种方案使用场景有限,在银行很少使用这种方案。

2.6 提升硬件处理能力

提升硬自身的处理能力当然在一定程度上可以提高并发的处理效率,解决热点账户的问题。

3 热点账户案例设计

根据以上6种设计方案,结合实际工作实践,分享一个热点账户处理的实践案例。

3.1 概要描述

以微信发红包为例,本方案采用了缓冲记账+汇总入账的方式

A客户用宇宙行的银行卡在微信上给A客户在地球行的银行卡发红包转100元,在系统中的流程是:
热点账户应对方案 - 图5

  1. 微信通过网联通知宇宙行从A客户下账100元,
  2. A客户下账成功
  3. 同时从微信开在宇宙银行的网联待清算资金内部户上账100元,
  4. 记账成功后宇宙行通过网联返回成功标识给微信,
  5. 微信通过网联告诉地球行给A客户转100元,
  6. 地球行通过微信开在地球行的网联待清算资金BGL内部账户,下账100元,
  7. 然后给A客户上账100元。

这个过程是信息流过程,也就是支付过程,这个过程是实时的;整个这笔交易还包括资金流过程,也就是清算过程,即宇宙行和地球行之间真正的账务清算,这个过程是非实时的,一般在一天交易结束后批量做清算。

3.2 案例描述

当A~N多个客户同一时间在微信上用宇宙银行的卡发红包,在宇宙银行开立的微信BGL待清算资金内部账户就需要在短时间内多次更新账户余额以及登记流水,这个内部账户就成为了热点户。

热点账户应对方案 - 图6

我们采用缓冲记账和汇总入账的方式解决这个热点账户问题。

这里我们利用一个CGL账户作为过渡,CGL账户可以理解为一个登记簿,相当于一个资金池,不计余额,这样我们可以把所有客户的请求记录insert到CGL账户中,然后等晚间批量的时候将CGL流水信息汇总成一笔计入BGL账户中。在这个方案中客户账记账和CGL记账必须是同一个事务,即同时成功或者同时失败,防止发生差错账。

这样只要CGL记账成功后就返回成功信息给微信,微信告诉地球行给A‘客户入账100元,客户体验到的交易是实时的,提高了客户体验,解决了热点账户的问题。
热点账户应对方案 - 图7

3.4 案例扩展

根据上面这个简单的案例描述再补充描述一个具体的扩展案例,本方案采用CGL实时记账首先解决账户热点问题,通过业务系统中引入CGL销账码(具体在业务系统中实现)概念解决资金控制风险问题,通过单进程读取CGL流水上核心完成实际账户流水补记实现流水连贯性问题,方案预留CGL池横向扩展应对单CGL瓶颈问题。整体设计思路总结为用做CGL账户缓冲,用异步记账补记帐方式完成账务流水处理,CGL用于技术内部处理,实际账户(BGL或客户帐)用于业务部门账务处理。
业务系统通过分表处理防止因表操作造成瓶颈,为实现CGL挂账安全控制在方案实现上引入销账码概念。BGL或客户帐记账依赖CGL记账流水,在一个系统中由一个进程轮循完成

热点账户处理流程图:
热点账户应对方案 - 图8

  1. 渠道调用发触发交易,发送至同步转异步模块;
  2. 同步转异步模块首先判断,是否需要挂账处理;
  3. 如果需要挂账,先生成销账码,之后计算分表名,再登记账务;
  4. 如果不需要挂账,先计算分表名,再校验销账码;
  5. 以上(3)和(4)两个分支完成后,调用核心系统的CGL记账服务;
  6. 核心系统接收到服务请求之后,进行处理对CGL账户进行记账,之后将结果返回同步转异步模块;
  7. 同步转异步模块收到核心系统的返回信息之后,更新分表;
  8. 更新分表完成之后,完成记账处理;
  9. 以上步骤为CGL记账,多进程并发处理。
  10. 以下步骤为BGL记账,为单进程轮询处理;
  11. 同步转异步模块在完成CGL记账之后,对BGL或客户帐记账进行处理,首先抽取数据;
  12. 获取数据后判断原交易是否超时;
  13. 如果原交易未超时,则交易流程执行步骤(17)判断源是否成功;
  14. 如果原交易超时,则先向核心系统发起查询结果请求;
  15. 核心系统收到查询结果请求之后,将结果返回给同步转异步模块;
  16. 同步转异步模块收到核心系统的查询结果之后,对交易结果进行修正,之后调用步骤(17) 判断源是否成功;
  17. 判断原交易是否成功;
  18. 如果原交易未成功,执行步骤(22)流程结束;
  19. 若果原交易成功,则调用核心系统的BGL或客户帐记账服务;
  20. 核心系统接收到服务请求之后,进行处理对BGL账户或客户帐进行记账,之后将结果返回同步转异步模块;
  21. 同步转异步模块收到核心系统的返回信息之后,更新交易结果之后执行步骤(22)流程结束;
  22. 流程结束。