账务系统热点帐号问题

账务系统的热点帐号是一个很常见的问题,特别是随着互联网支付的发展,热点帐号的问题更加突显。很多支付机构由于业务限制往往在一个银行只有一个对公帐号,这一个帐号往往就是热点帐号。

对于不同帐号的并发处理相对比较简单,比如按客户或帐号拆表、拆库,这之间比较复杂的是分布式事务问题。目前处理分布式事务一般的解决方案包括:

  1. 可靠消息+补偿模式;
  2. TCC分布式事务模式;
  3. 两阶段提交;
  4. 拆分为多个局部事务。

这些方案往往通过自动任务来处理异常的场景从而保证最终一致性。

如果说去IOE最困难的问题应该就数热点帐号问题了(或者说分布式事务问题),因为单个帐号的扣减余额等肯定要通过事务来保护,防止出现余额和流水不符的情况。但事务必然会导致并发度下降,即使对于功能很强大的主机核心系统,并发度也会显得捉襟见肘。为了防止自己处理能力超限,主机往往会进行流控以保护自己,但这样的用户体验会很差。如果这件事在开放系统中做,由于每个服务器性能有限,并发度会更低。下面介绍下自己对热点帐号问题的理解,因我本人并不负责核心系统,有不当之处欢迎指正。大体解决方案分为如下几种:

1. 汇总记账方式

汇总记账分为两种,收单和付款。其中收单采用异步入账的方式比较好,可以将钱先放在内部过渡户中(过渡户可以是一个或多个),然后异步将钱整批汇总后入收方帐号。对于付款会复杂些,主要涉及到透支的问题,如果业务上允许透支也可以采用先银行垫付一段时间,然后整批一笔入付方帐号;如果业务不允许透支就比较复杂了,一种可行的方案是将每笔账务先预先记录流水然后返回“处理中”,后台采用批量的形式以批量为单位更新付方帐号的余额。但这样带来的一个问题是响应不够及时。

2. 异步缓冲记账

参考资料1中提到的削峰填谷方式,思路比较简单,对于不超过并发数的交易同步响应,对于超过并发的交易放在缓存队列慢慢处理,可以返回处理中或成功(返回成功有透支风险)。这种方式本质并没有提高系统的并发量,只能算是一种简单体验改进。

3. 同步批量缓冲记账

在参考资料2中提到了一种可行的解决方式,但并没有介绍如何实现,结合参考资料3,可行的解决方案如下。当接到客户的转账请求后,如果为热点帐号,则在执行完所有的校验后,将交易放在一个队列(或多个队列)中。针对每个队列会有单一的线程进行处理,它一次或获取多条待处理记录然后统一记录数据库流水,对于热点帐号的余额扣减只扣减一次。对于这种方法如何确定队列的大小以及同步等待的时间是一个需要分析的问题。这种方法可以成倍减少数据库的压力,类似与数据库事务管理器的提交机制,但它也带来一个问题就是增加了单笔交易的延时。为了使这种方法收益最大化,在框架设计层面需要把同一个热点帐号的交易转到特定的服务器来处理。

4. 建立多个影子帐号

上述“同步批量缓冲入账”方式的限制是单台应用服务器的处理上限,单台服务器的处理上限。为了解决此问题,一种可行的解决方案是将一个热点帐号拆分为多个影子帐号,每个影子帐号有自己的余额和流水信息。所有影子帐号的余额之和是整个帐号的余额。拆分后的处理逻辑和单个帐号的处理方式相同,可以采用不同的处理方式,如异步缓冲记账或是同步批量缓冲记账等。这个方案需要特殊考虑的点包括:

  1. 帐号余额增加时如何分配给其他的影子帐号:为避免分布式事务,建议采用异步方式处理。当然由于这个交易量很少,采用分布式事务也是可行的。
  2. 当单个影子帐号余额不足但整体帐号余额充足时的处理方式:可以采用“直接报错“”或是“返回处理中”改为异步处理。对于客户的特殊提取交易不应直接报错,因为报错则无法支持客户转出所有余额。对于异步处理的方案在返回处理中时可将此笔账务交易转移到其他影子帐号处理,如果所有影子帐号均余额不足但整体余额充足时,此时需要进行影子帐号间调账,将此笔交易放入特殊队列,将此影子帐号置为停用,在处理时将此影子帐号的钱转给其他影子帐号然后继续处理。直到所有的帐号均不支持则进行报错。可以看出当开始停用影子帐号时系统整体的并发度会显著降低,所以采用影子账号应让客户知晓,并提前做好客户的工作,例如帐号至少要保留特定余额、指定特殊的销户流程、与客户约定好各种异常的处理策略等等。
  3. 多个影子帐号之间需要保证负载均衡问题:另外需支持动态增加和减少影子帐号的个数。

另外提一下,内存数据库并不是一个好的解决方案,首先如何保证内存数据库和关系数据库的数据一致性是一个困难的问题;另一方面内存数据库本身也是一个单点,会存在单点问题;再一方面关系数据库本身对于热点数据也会做内存级别缓存,所以内存数据库意义并不是很大。

上面介绍了很多方案,可以看出并没有完美的方案,热点帐号方案更多的考虑是一种权衡,我们需要根据不同的情况来指定不同的策略。后续我会试着对于方案三、四进行实现,看是否有其他问题。后面如发现更好的解决方案也会更新此文章,也欢迎大家告知其他解决方案。

参考资料:

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》

留言:

(lesstile enabled - surround code blocks with ---)