地崩山摧壮士死,然后天梯石栈相钩连。
1 前言
在之前的文章中,已经讲述了不少关于账户设计的内容,热点账户是一个重点的内容,在实际的业务中十分常见。热点账户指的是被高频操作的账户,短时间内有大量的账户余额变更请求集中在极少数的账户上。热点账户一般指的是 B 端商户账户而言,商户账户关联的业务多且存在收付款时间集中的情况。
2 热点账户分类
账户根据资金的方向可以分为入账和出账,热点账户根据业务场景可能是出账频繁,也可能是入账频繁。
入账频繁场景: 常见的收单,比如电商商户,第三方支付公司等都可以理解为入账频繁,因为其入账多而出账少。
出账频繁场景: 是一些扣费类的场景,比如提供短信服务、广告计费服务、流量扣费类的账户,这些账户是一般是按照频次、流量收费,一般都是小额交易。
除上述两种账户之外,还有一种入账和出账都频繁的账户,这类账户具有以上账户的特点。
3 热点账户解决方案
1 限流方式
当业务请求超过系统的承载能力后选择丢弃,本质上是将技术问题转换为业务问题,牺牲了一部分的业务体验,保障业务系统的正常运行。
2 缓冲方式
本质是削峰填谷的手段,将正常的业务请求同步转异步(mq/线程池)进行处理,用于平抑业务处理的压力,能够以恒定的速率处理请求。可以使用消息队列(消息队列可能导致消息丢失)、异步线程池的方式来处理,这些是入账实时性不高的业务场景,适应于账户增加的余额的场景,对于扣减的场景并不适用。
3 汇总方式
该方式是将交易记录记录在数据库表中,并不直接更新账户余额,而是以一定周期的方式进行汇总,这种处理方式在首单的场景下比较常见,通常是 T+1 的入账方式, 要在入账多、出账少的场景下使用,避免账户的透支。
4 暂存方式 将账户的入账请求记录在数据库中,使用定时任务处理账户的入账操作,此种方式要求用户的入账记录有唯一记录的请求单号,不仅方便入账的操作,对后续的查询和对账工作也有帮助。暂存的方式,账户的余额放入缓存中,这样可以保证每个服务获取到的余额均是最新的值,提高系统的性能。
5 分治方式 将商户分为多个子账户,每个账户中的余额足够,通过负载均衡算法将请求分到每一个子账户中,这样可以提高账户的处理速度和性能。对于账户的入账操作,在高峰期可以根据时间或者用户id 取模路由到商户的子账户,记录流水并操作子账户余额,然后在业务低峰期将子账户余额转移到主账户中,即可实现入账热点账户的问题。对于账户出账热点问题,分为两种情况:1、单个商户账户批量出账的操作,这个和积分分发的场景类似,可以通过批量操作实现,即修改一次账户余额,批量写入操作日志来解决。2、多个账户临近时点的操作,也可以路由到子账户进行处理,如果子账户金额不够,按照顺序路由到下一个子账户操作。与此同时,需要将子账户的余额归集到下一个账户中。
4 总结
在本文中,结合之前讲述的 B 端和 C 端账户的实践,分享了热点账户的解决方案。值得注意的是,账户余额的操作一定要使用悲观锁来操作(在业务场景必须要使用事务来解决的情况下)。本文中所涉及的代码已经上传至 github, 欢迎交流学习。项目地址 springboot-auth。