借助Redis做秒杀和限流的思考
最近群里聊起秒杀和限流,我自己没有做过类似应用,但是工作中遇到过更大的数据和并发。
于是提出了一个简单的模型:
var count = rds.inc(key);
if(count > 1000) throw "已抢光!"
借助Redis单线程模型,它的inc是安全的,确保每次加一,然后返回加一后的结果。如果原来是234,加一了就是235,返回的一定是235,在此中间,不会有别的请求来打断从而导致返回236或者其它。
其实我们可以理解为inc的业务就是占坑排队,每人占一个坑,拿到排队小票后看看是不是超额了,再从业务层面输出秒杀结果,甚至做一些更加复杂的业务。
六条提到限流,可能基于某种考虑,希望把key对应的count给限制在1000附近,可以接受1%偏差。
于是有了改进模型:
var count = rds.inc(key);
if(count > 1000){
rds.dec(key);
throw "超出限额!"
}
就加了一句,超出限额后,把小票给减回去^_^
采用Redis有一个好处,比如支持很多应用服务器一起抢……
当然,对于很大量的秒杀,这个模型也不一定合理,比如要枪10万部手机,然后来了300万用户,瞬间挤上来。
这里有个变通方法可以试一下,那就是准备10个Redis实例,每个放1万。用户请求过来的时候,可以随机数或者散列取模,找对应实例来进行抢购。
同理可以直接更多用户的场景。总的来说,在数据较大的时候,随机和散列就具有一定统计学意义,相对来说是比较均衡的。
上面是大量秒杀的简单场景,那么小数据场景呢?比如就只有几万并发的场景。
小数据场景,单应用实例,可以考虑把Redis都给省了。
初级模型:
Interlocked.Increase(ref count);
if(count >= 1000) throw "抢光啦!"
中级模型:
private volatile Int32 count;
var old = 0;
do {
old = count;
if(old >= 1000) throw "抢光啦!"
}while(Interlocked.CompareExchange(ref count, old + 1, old) != old);
这个CAS原子操作可是好东西,在x86指令集下有专门指令CMPXCHG来处理,在处理器级别确保比较和交换数据的原子性。大多数系统想要迈过10万tps的门槛向100万tps靠齐,就必须得实现无锁操作lock-free,其中CAS是最为简单易懂,尽管有时候有ABA问题,但我们可以找到许多解决办法。
在实际使用场景中,可能有更复杂的需求,那就另当别论,这里只能班门弄斧几个简单易用的模型。
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!