本文结构很简单:
5张图送你5种秒杀系统,再加点骚操作,再顺带些点心里话♀️。
一个简单的秒杀系统实现原理: 通过redis原子操作减库存
图一
优点 缺点 简单好用 考验redis服务能力
是否公平 公平 先到先得
我们称这类秒杀系统为:
简单秒杀系统
如果刚开始QPS并不高,redis完全抗的下来的情况,完全可以依赖这个「简单秒杀系统」。
一个够用的秒杀系统实现原理: 服务内存限流算法 redis原子操作减库存
图二
优点 缺点 简单好用 -
是否公平 不是很公平 相对的先到先得
我们称这类秒杀系统为:
够用秒杀系统
性能再好点的秒杀系统实现原理: 服务本地内存原子操作减库存
服务本地内存的库存怎么来的?
活动开始前分配好每台机器的库存,推送到机器上。
图三
优点 缺点 高性能 不支持动态伸缩容(活动进行期间),因为库存是活动开始前分配好的 释放redis压力 -
是否公平 不是很公平 不是绝对的先到先得
我们称这类秒杀系统为:
预备库存秒杀系统
支持动态伸缩容的秒杀系统实现原理: 服务本地协程Coroutine定时redis原子操作减部分库存到本地内存 服务本地内存原子操作减库存
图四
优点 缺点 高性能 - 释放redis压力 - 支持动态伸缩容(活动进行期间) - 具备通用性 -
是否公平 不是很公平,但是好了点 几乎先到先得
我们称这类秒杀系统为:
实时预备库存秒杀系统
公平的秒杀系统实现原理: 服务本地Goroutine定时同步是否售罄到本地内存 队列 排队成功轮训(或主动Push)结果
图五
优点 缺点 高性能 开发成本高(需主动通知或轮训排队结果) 真公平 - 具备通用性 -
是否公平 很公平 绝对的先到先得
我们称这类秒杀系统为:
公平排队秒杀系统
骚操作上面的秒杀系统还不够完美吗?
答案:是的。
还有什么优化的空间?
答案:静态化获取秒杀活动信息的接口。
静态化是什么意思?
答案:比如获取秒杀活动信息是通过接口 https://seckill.skrshop.tech/v1/acticity/get 获取的。现在呢,我们需要通过https://static-api.skrshop.tech/seckill/v1/acticity/get 这个接口获取。有什么区别呢?看下面:
服务名 接口 数据存储位置 秒杀服务 seckill.skrshop.tech/v1/acticity… 秒杀服务内存或redis等 接口静态化服务 static-api.skrshop.tech/seckill/v1/… cdn、本地文件
以前是这样
变成了这样
结果:可以通过接口https://static-api.skrshop.tech/seckill/v1/acticity/get就获取到了秒杀活动信息,流量都分摊到了cdn,秒杀服务自身没了这部分的负载。
小声点说:“秒杀结果我也敢推CDN。”
备注:之后我们会分享`如何用Golang设计一个好用的「接口静态化服务」`。
总结上面我们得到了如下几类秒杀系统
秒杀系统 简单秒杀系统 够用秒杀系统 预备库存秒杀系统 实时预备库存秒杀系统 公平排队秒杀系统
我想说的是里面没有最好的方案,也没有最坏的方案,只有适合你的。
拿先到先得来说,一定要看你们的产品对外宣传,切勿上来就追逐绝对的先到先得。其实你看所有的方案,相对而言都是“先到先得”,比如,活动开始一个小时了你再来抢,那相对于准时的用户自然抢不过,对吧。
又如预备库存秒杀系统,虽然不支持动态伸缩容。但是如果你的环境满足如下任意条件,就完全够用了。
秒杀场景结束时间之快,通常几秒就结束了,真实活动可能会发生如下情况: 服务压力大还没挂:根本就来不及动态伸缩容 服务压力大已经挂了:可以先暂停活动,服务起来&扩容结束,用剩余库存重新推送运维自身不具备动态伸缩容的能力所以:
合适好用就行,切勿过度设计。