高并发热点账户分析
[吃瓜R]之前面试ZJ问了一个热点账户问题,答得不好,今天就来好好分析下热点账户到底该怎么设计
[得意R]什么是热点账户
同一个账户,短时间内,大量的入账和出账
[吃瓜R]账户写入基本流程
事务内完成账户余额的变更与操作明细的记录,期间至多有一个线程抢占资源,其余线程block或直接返回失败
[吃瓜R]解决热点账户的思路及优缺点
1.限流。
-- 既然顶不住那么高的流量,那就限制写入的流量吧。
-- 优点:账户的写入是可控且正常的。
-- 缺点:高并发时,由于限流会导致转账失败率极大提升,对用户极不友好
2.先插入,后统一汇总写入
-- 数据库的insert几乎无资源竞争,先写入到一张流水表中,后比如10s,统一汇总到账户中
-- 优点:对于频繁入账的性能提升显著
-- 缺点:出账场景如果不判断当前余额,可能会出现账户透支的可能性。
3.缓冲写入
-- 某一时间段突然的大量写入,那就先写入mq中,以数据库事务可承受的速率消费消息写入账户
-- 优点:削峰填谷,控制了高并发抢占资源的问题
-- 缺点:1.mq消费消息失败,结果需要另行通知用户 2.如果账户一直是热点账户,就不存在谷这么一说,mq的削峰填谷也就无异议了
4.拆分成多个子账户
-- 单个账户只有一个资源,那就拆分成多个子账户,抢占被分摊的资源变多了
-- 优点:hash进行账户的负载均衡,解决了部分抢占同一个资源的冲突的问题
-- 缺点:子账户余额较分散,单一子账户余额不足,但是整体账户其实是充足的,因此可能会影响转账成功率,且多个子账户的操作,处理起来较麻烦
5.使用内存数据库
-- 问题的原因是磁盘写入慢,那我就直接内存写入
-- 优点:写入速度大幅增长
-- 缺点:断电即失,没有数据库事务保证,得详细考虑边界场景,因为资金安全是及其重要的。
6.提升单台机器的性能,升级CPU
-- 只是一个权宜之计,解决不了根本的问题
[微笑R]简单设计一下
背景:3笔加100元,1比减200元的同时来了。
设计额外数据结构:
1.操作流水表(记录每笔交易记录)
2.redis缓存(记录每个账户的余额)
入账则只记流水表
出账从redis缓存中判断当前余额(直接参与计算>0则足够,<0则不足(不足的话先记录到重试队列,等至少汇总一次后重试判断如果还不足则返回不足))
+100的写入流水表中,-200时此时缓存余额不足,记录到重试队列中,并且缓存余额需要加回来200(如果加回来失败呢?后续有什么手段确保成功)
10s后进行汇总,锁定账户资源,入账流水等账户写入成功后,加入缓存中
讨论如下方案的区别?各有什么好处吗?
入账不直接写缓存,而是等10s后汇总时再写入
入账直接写缓存
入账直接写入缓存
-
实际账户并没入账
-
redis缓存余额脏数据很难替换(也是筛选入账)
边界点:
-
redis写失败了,缓存中的脏数据有什么机制替换
-
以入账在汇总时再写入缓存为例
-
每过5min/有redis写入失败时?,就重新替换一波。
如何替换?
-- 生成一个新的redis的key,如果新key存在,则后续的出账操作全部返回失败,并落入重试队列中,原key的计算=汇总后数据库余额即可
-
-
redis挂了,这套流程还能否正常运转
-
mq策略(先接收数据库慢慢处理,出账也是按照顺序逐条,异常则落重试队列)
-
兜底限流,在数据库可承受范围内事务转账
-