让“热点账户”清凉一夏
编辑导读:“热点账户”来源于银行领域,指的是银行或支付账户等需要进行代收付等业务的中间内部账账户。本文作者围绕“热点账户”展开分析,提出一些常见的解决思路和案例,希望对你有帮助。
我们经常听到一个概念“热点账户”,可能很多人不陌生什么是热点账户,但是对如何解决热点账户问题并没有系统性的完整的思路。
事物总是发展的,事物所处的场景也是在不断变化的,基于有限的经验去解决无限的可能性是很好的技能。我们试图从认识热点账户开始,再了解几个常见的解决思路和案例,打开对热点账户的世界。
一、关于账户
什么是账户这个话题就不展开讨论了,我们之前也写过很多跟账户相关的内容。
了解账户的结构是“入账请求、账户流水、账户余额”;入账请求产生了账户流水,账户流水更新账户余额;所以其中我们要关注账户流水的创建以及账户余额的更新,这里账户余额的更新要重点关注,这是后续造成热点账户发生的主要的环节。
账户的账务处理主要分三类:收钱,付钱,账户间转账;收钱针对一个账户的余额增加,付款是一个账户的余额减少,转账是两个账户之间一个增加一个减少。
账户处理的存在最大并发量,超过这个并发量账务处理就会出问题;账户账务处理过程的线程控制,无论是收钱、付钱还是转账,为了保证账务的准确性,每次账务处理都是一个单独的事务,就算是多个请求同时发生,对账户的操作也是一个一个的来;当一笔入账请求开始处理时账户资源会被加锁,等该笔请求处理完以后会释放锁;这样的话就意味着每一笔账务处理都会获得一个时间占用,这个时间内其他入账是不能操作的,这样就出现了资源的瓶颈,也就是账户的入账必然存在一个并发的最大阈值,一旦超过这个阈值,就会出现账户的性能问题。
热点字段和热点事件;在交易并发过程中,有些字段的使用频率很高,比如流水号、余额、发生额等字段,这些使用频率很高的字段称为热点字段;导致热点字段的事件称为热点事件,比如商家结算日,需要给商家结算付款,这时候造成付款专户的付款并发,这个就是一个热点事件。
二、热点账户定义
业务发生以后会产生业务事件,比如下单支付,业务事件需要申请入账;当在短时间内产生了大量的业务事件,或者狭义的看有大量的交易产生时,造成高并发的入账请求,高并发引起了账户系统的性能瓶颈而产生了性能问题,进而造成入账延迟、失败、账务准确性等各种账务问题;而这个过程中涉及到的账户我们就称为热点账户;所以说热点账户是由于高并发交易时频繁更新账户产生性能问题而造成的,所以这里一个关键的前提是“高并发”,那么热点账户的标准是什么呢,在一些文献里提到以下标准。
- 账户每秒有10次以上更新需求
- 串行化时账户处理延迟高于1秒以上
我想热点账户产生的根本机理跟城市交通高峰时段的拥堵地段是一个机理,怎么解决高峰问题,错峰出行,限号限流,分流等等;同样热点账户也是,既然是高并发造成的拥堵,我们就可以以降低账户上的并发为目的,也就是降低热点账户的操作并发以降低账户热度;像可以缓存记账,可以批量汇总记账,可以将账户拆分分摊并发,可以进行记账排队等等手段都可以降低对账户的高并发操作,从而降低账户热度。
三、热点账户常见发生场景
既然产生热点账户的原因是高并发造成的账户被频繁更新,所以我们探索热点账户的发生场景就转换成了探索交易高并发的场景,这些高并发场景都有可能造成热点账户,比如很容易想到“双11购物节”“微信发红包”“某自营电商平台的秒杀抢购”“小米官网新品的预售”等。
这里的场景有的是因为高并发收款造成的,有些是高并发付款造成的;而且收款和付款的热点账户解决方案往往会存在差异,不同场景的收款和付款解决方案也会有差异;就像淘宝双11收款,淘宝中间账户其实因为不外漏给用户,后续商家履约周期也长,所以时效性要求并不高,可以考虑批量定时入账、异步入账、缓存入账等,不采用实时入账;所以我们可以将业务场景分为高频入账场景和高频扣款场景;像淘宝双11的中间担保账户就是高频入账造成的热点账户。
我们来分析一下淘宝双11产生热点账户的场景。
我们在淘宝购物时都清楚,钱是不会直接给到商家的,而是先收到中间担保账户,等履约完成以后才会由担保账户结算给商家,所以这里对于担保账户来说有两个过程,一个是用户购买商品时的收款,另一个是服务履约以后的结算付款,而其中双11期间的用户付款就成了热点事件,从而中间担保账户的收款造成了担保账户成为热点账户。
上面我们介绍了,淘宝担保账户是内部账户,并不外漏给用户或者商家,这样的话,只需要实时告诉用户和商家已经付款成功即可,至于担保账户的入账可以慢慢的处理;这里我们也得到了一个思路,解决热点账户要关注两个问题。
一个是给用户的反馈:对交易参与者的反馈策略,参与者需不需要实时知道账户的处理情况,是只需要知道结果即可还是需要实时看到账户余额的变化,显然对于淘宝中间账户来说,账户参与者是不需要知道账户余额更新情况的,只需要知道支付成功的反馈结果即可,而这个结果可以依赖渠道的反馈通知,而内部记账可以不反馈给用户,这样的话担保账户就有了足够的时间和选择来规避并发问题。
另一个是账务要求:账务更新时效性要求高不高,账务的准确性要求高不高,这里中间担保户对时效性要求不高,准确性肯定是所有账户要求都是很高的。
这样我们可以使用批量的延迟更新中间担保账户的方式来规避双11高并发交易的入账请求,以规避热点账户的发生。
这里我们要知道个事实,数据库插入数据的并发支持是非常大的,一般不会造成数据库性能问题,批量插入即可,只是更新账户余额需要进行锁处理,会造成性能问题,所以解决淘宝中间账户的关键是解决账户余额更新问题。
首先我们先将高并发的交易的入账请求插入到入账流水表,这时并不着急去更新中间担保账户余额,此时这些流水我们标记一个入账状态“未入账”;定时的去扫描这个流水表,将扫描到的数据进行加锁,确保不会被后续入账或其他处理影响,然后汇总求和,用这个求和的总值去更新中间担保账户的余额,然后将这一批“未入账”流水更新为已入账,然后释放锁,这样就以很小的并发完成了对中间账户的更新。
下面我们介绍几种常见的解决热点账户的方案,并且针对每个方案我们列举一个实际的案例为补充;对于热点账户的方案设计过程中,我们需要重点关注几个问题:
- 收支:账户是收入热点账户还是支出热点账户
- 内外:账户是用户热点账户还是内部热点账户
- 时效:账户是高时效性实时入账还是不需要高时效性
- 结果:用户是否需要实时知道入账结果还是不需要
- 余额:用户是否需要实时知道账户余额更新还是不需要
对于方案的设计和选择可以先做以上几个方面的分析,然后选择合适的解决方案;比如上面淘宝中间担保账户对时效性要求不高,用户不需要感知余额的场景我们就可以选择延迟批量汇总入账。
对于方案的设计,我们就可以基于以上几个问题从解决“账户的单位并发”为核心突破点,因为并发造成的频繁更新是热点账户产生的根本原因,所以解决的热点账户并发的问题也就可以解决热点账户的问题。
四、限号限流-直接控制并发
第一性原理,不想太多,直接解决产生问题的问题本身;并发不是造成热点账户么,那么反向思考,这个并发阈值是多少,在账户之前设到屏障控制这个阈值,开闸放水,门就这么大,只能进来这么多水,就像很多城市的限号限流一样,最终的结果肯定是损害一部分车主的体验;虽然是立竿见影的效果,但是这也是自损800的方案;这里我们不妨称之为“热点阀”。
所以给账户安装“热点阀”可以解决热点账户问题;但是问题还是比较明显的,除非你很强势,比如交通控制,否则以服务为第一客户优先的企业这种方式一般不会采用。
五、变多为少-明细汇总记账
数据库插入数据是可以支持非常高并发,可能达到3.2w/s,所以插入流水不是热点账户瓶颈所在,而是余额更新;所以我们就将“降低账户的单位并发”设计思路缩小到了“降低余额更新并发”的范围;对于余额更新就是基于流水去增加或者减少余额,了解C语言的应该知道,无非就是下面的这个函数(不一定准确,但是这么个意思)。
balance=balance+发生额
就像新余额等于当前余额加上或减去该流水的发生额,降低余额的更新频率我们自然就可以想到,可以降低发生额的更新频率,也就是你们这个多人来更新我,我实在是忙不过来,都堵在门口,不如你们派一个代表10分钟进来一次,汇总大家的要求我一次性解决;这样就是我们说的“汇总明细记账”的方法,这样的话这个函数从意义上就变成了。
balance=balance+sum(一段时间内全部明细的发生额)
这个方案的适用场景就是不需要实时更新余额,且主要是增加账户余额的场景如果是扣款类场景,可能汇总入账会造成账户余额透支,不过如果允许账户透支,出款类场景也是可以考虑,只不过对资金管理的时效就会变差,你无法从账户余额直接看到剩余可用头寸。
六、排队办理-缓冲记账
现在大家经常做核酸,因为并发较大,检测人员有限不能实时采集样本,所以大家需要排一个很长的队伍;同时因为一个人一个人检测成本也高,效率也低,所以10个人一组进行混采;混采就像我们上面说的指派代表的汇总记账一样;所以说我们可以为账户设置一个排队机制,出现高并发账户更新不过来时大家进行排队办理。
就算排队可以解决账户的并发问题,但是肯定是有天花板的,就像10个人采样,100万人排队,那要做到什么时候,也可能发生抱怨和投诉;所以怎么办呢?增加检测点是一个很好的办法,1000个人采样,就可以分摊这么多的要检测的排队的人;这就是我们下面要讲的“子账户拆分”分摊压力。
七、临时存放点-缓存入账
高并发请求可以先进行缓存,然后定时将缓存更新到数据库;这个看起来跟缓冲记账异曲同工,但这里有个区别,缓冲记账时账务请求还在排队入账,而缓存记账实际上账务请求已经实时在缓存中完成了记账;缓存记账具备缓冲记账和汇总明细记账的双重优点。
这里要注意因为缓存需要具备账户余额部分的管理,所以账户系统的余额要赋予缓存模块,缓存模块在此余额基础上进行出金和入金的记账操作;定期将缓存同步到账户系统完成最终的记账,记账完成的缓存部分可以进行清空,然后获得最新的账户余额,以此循环。
八、增加点位-子账户拆分
当即要解决高并发的热点账户问题又要保证实时性要求时,上面的会影响账户更新时效的方案自然就不是首选了,那么就需要一个能够支持实时余额更新的方案了;既然一个账户的更新出现了瓶颈,那是不是可以考虑为他找更多的帮手分摊压力呢?答案是肯定的;我们可以将账户进行按需拆分,拆分成多个子账户,将高并发请求分配给各个子账户,从而每个子账户的并发就降下来了;这里要明确一个问题,这个子账户更多是不能让用户感知的,只是内部的处理方案,对用户来说还是一个账户,所以流水和账户余额反映给用户的都是一个;这就意味着:
账户作为一个整体被用户感知。
这样的话势必要增加很多账户设计的复杂程度,比如入账的时候入哪个子账户,是平均入账还是按序入账,出账的时候怎么出,有个别子账户余额不足但是总账户余额充足时怎么处理;所以说这里会有一个复杂的账务处理模型;不管这个模型怎么设计,要保证金业务上顺利完成账务记录要求以及用户的使用体验,从而确保业务的正常进行。
我之前设计的账户系统,每个商家可能会有七八个子账户,出款的时候是按照顺序出款,就是先扣完一个账户扣下一个账户。
想起在某支付机构做了断直连接入网联的改造项目,其实网联的项目方案里提到了其要作为人行支付系统的前置系统做人行热点账户系统的前置缓冲,我想这个设计方法是不是可以借鉴到工作当中。
九、小弟先上-前置缓冲
为解决备付金集中存管所形成的热点账户问题,实现对已映射额度管理,网联构建了“备付金热点账户前置系统”即“RCMP”,用于支付机构通过网联平台(EPCC)的业务办理。
前置系统分为额度管理模块及账户管理模块,网联将为各支付机构在前置系统中建立账户,用于可用额度的监控、已映射额度的管理。
因为网联是“实时清算,定时结算”,在一个清算周期内净额轧差了支付机构的支付指令,最后提交给人行的结算请求笔数一个周期从每个机构的上千万笔到一笔,这个看起来有点像汇总明细记账,而这个汇总处理的操作是网联代人行支付系统完成的。
那这么看,是不是我们工作当中也需求这样一个前置系统,来帮助账户系统完成压力的分担,比如要求业务方建设前置模块,按照要求进行处理加工后请求入账;特别是账户中台,面对众多业务线时,是不是可以考虑每个业务线在某些场景下都做账户前置,完成初步加工后再请求中台账户系统,这样就可以分担中台的压力,又不损害账户的中台核心能力,业务线也不用完整建设账户系统;这里的思路就是中台不能一揽子大包,一些能力要分摊给业务线,比如业务线的个性化部分,需要业务线做前置层。
十、打铁还需自身硬-技术性能升级
技术层面就不过多讨论了,作为非专业人士,或者说产品视角提些建议“能多增加几台服务器么,可以扩充下硬盘么,你这个cpu可不可以换成顶尖的……”
事物总是变化的,场景也是不断地更新,面对新的场景,新的问题,历史的经验只能作为解决新问题的参考;不妨在遇到虚拟问题时多思考一下真实的世界,也许会有意料之外的答案!
#专栏作家#
陈天宇宙,微信公众号:陈天宇宙,人人都是产品经理专栏作家。多平台支付领域专栏作者,十年资深产品;天使投资人;专注为10万支付产品经理和支付机构以及企业提供深度支付内容和服务!
本文原创发布于人人都是产品经理。未经许可,禁止转载。
题图来自Unsplash,基于CC0协议。