发布时间:2024-12-21 09:01
本文讨论的场景是互联网业务中秒杀场景下热key的解决方案,秒杀场景不局限于电商活动,也包括:日常活动下发奖品的配额控制、春节的集卡瓜分现金、春晚的摇一摇抢红包等。更进一步的说,是探讨所有关于有热key的场景的一种解决方案。
以上图为例,解释下各个模块: 发货模块:先查询库存,有库存后给用户发货,进入到账流程。
到账模块:把钱发到用户账户上。
使用模块:用户看到账户上的余额,进行提现/使用操作。
发货流程中,有高并发流量,有热点key。本文重点讨论这个流程。
到账流程中,有高并发流量,无热点key。一般只有库存数量的请求才会进入到这一步,用户的请求已经少了很多,而且这里可以按用户分区治理解决高并发问题(分set分库分表等)。本文不讨论此流程。
提现流程中,用户到账后,有提现需求的用户才会发起请求,本文不讨论此流程。
按照业务规模和流量预估,将场景分成如下几类,分别探讨下热点的解决方案。
有库存时,已经抢到的人直接读取抢的结果; 没有库存时,没抢到的人直接根据读取的库存的结果,返回已经抢完。 还要谈一下安全的问题,接入层的节点可以定时cache黑产名单,提前拦截掉请求。
这个qps下,库存的DB是瓶颈,假设采用redis,单key可以扛住10wqps的读写,用redis存储库存信息。 抢到的用户才会进入到账,这部分的并发量取决于库存数,因此这里开始把发货和到账流程分开。用户流程分几步:
如果扣件库存失败了,怎么处理?
这个qps下,redis的单key也存在瓶颈了,可以采用分治的方法解决热key问题。 一般有两种方式:
这里提出基于瞬间高并发下热key的多分片本地cache方案,如下图所示。
库存存储按key分片,分片公式参考:max(min(库存数, qps/10w), 1)。新加进来的库存节点,从库存db中将数据loading到本地cache住。发货访问库存步骤如下:
如果本地分片cache都是0,代表此时库存已经没有了,直接返回上游已经瓜分完毕。 本地的分片cache本质上是个bloom滤波器,告诉你有库存时不一定有,但是告诉你没有库存时一定没有。刚好应对高并发流量下,大量抢不到商品的用户应该快速返回告知用户结果。
这个方案也解决了用户时有时无问题,也解决了先来的用户瓜分不到,后面来的用户可以抢到的问题。对db的方案压力也不大。
需要特别说明的是,当db无库存时,当本地cache有库存时,会触发扣减库存,如果10个本地cache都不为0,会触发10次的扣减库存IO,但是很快会收敛到本地cache和db一致的情况,我们测试了20wqps下的请求的放大倍数在1.003756倍。
管理员补库存后,本地库存节点会定时取到库存,cache在本地节点。
整体架构如下:
这里引入了瓜分服务,分担发货服务的压力,针对没有资格的用户、已经瓜分过的用户,提前把流量拦截掉。
这个量级的秒杀系统,我自己没有做过,跟大佬交流后参考大佬的做法: