一、平台热点账户的定义:
在第三方支付系统的账务数据库的处理中,数据从一个账户转出,或者有数据转入一个账户,账户都会收到记账请求,并都有一个记账处理的过程。记账处理过程主要包括两部分,一是记录记账凭证,二是更新账户的余额。为了保证账户不被其他请求影响数据的准确性,在进行记账处理时,会先对用户级账户的数据表加锁,记账处理完毕后会自动释放锁。随着系统日均账务处理业务量的增大,账务数据库中的账户常常会在瞬间产生多个并发操作,但所有对应的并发线程中只有一个线程能够持有当前账户的资源锁,其他线程必须等待该锁被释放后再逐一进行记账处理,这样该账户将会被频繁加锁释锁,使该账户成为账务数据库热点,产生性能瓶颈点,严重影响账务数据库的性能。同样地,对于公有云平台虚拟结算业务来说,由于会涉及营销/促销类的一些大型活动,也会出现大量活跃用户账户成为热点账户的情况。
记账处理的主要流程图如下:
二、热点账户的解决方案
我们可以知道解决热点账户带来的数据库性能问题就是要解决高并发环境下数据库锁争用问题,主要解决问题的设计方案可以从以下几方面来考虑:
1、对并发度进行限制
(也就是大型互联网平台通常所说的限流)
同一时刻,对同一账户修改(update)的请求数越多,这个账户的锁等待问题就越严重。可以考虑在前端页面上增加支付请求频率的限制来降低对账户的并发度。比如,可以考虑在用户点击“支付”/“提现”/“退款”按钮后,将发起异步查询线程来查看数据库的记账凭证中是否有成功的记录(凭证是只有在记账成功时候才会insert至账务凭证表)。如果凭证存在,则按钮可用。也即为通过限制用户的操作频率(发起支付、退款、提现动作的频率)来解决对用户账户的热点争用问题。
2、汇总账务流水记账
实时的交易全部是insert账户流水(insert的开销很小,能够支持高并发。如果基于分布式集群部署,insert的并发容量理论上可以比较),然后定时(比如每隔一分钟/每半个小时)将之前一分钟/半个小时内的账务流水sum出一个结算总金额,一笔入账结算到指定账户。
该方案的缺点就是:交易不能实时入账,其实如果控制好定时汇总入账的频度,比如分钟级,用户也是可以接受的。这种方式对充值类业务(账户加钱)非常实用,但是对支出类业务(账户减钱)类来说,有账户透支地风险。
3、缓冲入账
将实时同步的记账请求进行异步化,以达到记账实时性和系统稳定性之间平衡的记账手段,也就是“削峰填谷”。具体的来说,假如我们的系统对同一个账户的处理阈值为笔/分钟,24小时不间断服务(一天能处理笔)。当业务高峰期来临的时候(比如公有云平台的营销模块上线/或者搞类似双11/双12的大促),热点账务的请求数会达到~笔/分钟。当账户的交易低于笔/分钟的时候,系统几乎还是实时地处理了记账请求,而当交易大于笔/分钟的时候,系统可以先返回结果,把记账处理丢到可靠的缓冲队列中(这里可以是放在类似redis缓存/内存队列中),等并发量不大的时候慢慢消化,对用户来说感受到的体验还是很快就记账成功了(只是显示可用余额的时候有所数据差异)。
不过,这个方案是有个前提是:
热点账户在某几个高峰时间点需要缓冲记账来削峰填谷,并且能在日间填完。一旦账户的日间交易量暴增,导致日间队列根本来不及消化,整个队列越来越长,那就不存在谷可以填,这时候肯定会带来用户一些投诉。另外这种方案对支出类业务(账户减钱)来讲,也会有账户透支地风险。
4、建立影子账户分散记账的压力
具体来讲就是创建与热点账户对应的多个影子账户,所述影子账户与所述账户的数据结构相同,将所述影子账户设置为隐藏,并将所述账户的余额分散至各个影子账户。当账务系统接收到账务请求的时候,通过前置进行hash分配(具体的hash函数会有更多方案)选择影子账户进行记账,这样就将原来对一个账户的请求分散到多个影子账户中,分散了账务热点。
这个方案也有缺点:通过算法选择的影子账户扣款,影子账户的余额可能是不足的,但账户的总余额是够的,这样也可能影响账务处理的成功率。同一个热点账户下建立多级影子账户的框图如下:
5、提高服务锁并发能力
通过redis/memcache这种分布式缓存进行实时记账处理,然后启用异步线程将记账的结果同步至数据库中的账户表中。
以上是一些用于解决公有云平台用户热点账户问题的常用方案,大家可能会有一些更好的方案,欢迎大家交流共同学习。
谢谢阅读长按识别