基本概念
-
秒杀场景的业务特点是限时限量,读→读写(原子操作) 可以通过缓存兜住大量读\
-
秒杀场景
- 秒杀前
- 秒杀中
- 秒杀后
秒杀场景的负载特征对支撑系统的要求
-
特点
- 瞬时并发访问量非常高
- 读多写少,都是简单的查询工作(读库存数远远大于下单数)
Redis 可以在秒杀场景的哪些环节发挥作用?
-
秒杀活动的三个阶段:秒杀活动前+秒杀活动开始+秒杀活动结束后
-
秒杀活动前
- 场景特点:用户会不断刷新商品详情页
- 解决办法:把商品详情页的页面元素静态化,然后使用 CDN 或是浏览器把这些静态化的元素缓存起来
- 不会把请求真正打到redis
-
秒杀活动开始
-
场景特点:大量请求打到服务器,查询和扣减库存
-
流程
- 查询库存
- 扣减库存
- 生成订单,执行后续流程 从生成订单开始后需要通过数据库事务进行支持
-
此时库存数全部由缓存记录
-
-
秒杀活动结束
- 场景特点:可能还会有部分用户刷新商品详情页,尝试等待有其他用户退单
- 服务端可以轻松支撑
\
Redis 的哪些方法可以支撑秒杀场景?
-
根本需求
-
支持高并发
- redis本身支持高并发
- 将不同的实例保存在不同slot中
-
保证库存查验和库存扣减原子性执行
- 基于lua脚本实现原子性
- 基于分布式锁实现原子性
-
-
基于原子操作支撑秒杀场景
- 使用lua脚本
- 使用eval执行脚本
-
基于分布式锁支撑秒杀场景
- 大量秒杀请求会因为没有获取分布式锁而被过滤掉(取决业务代码怎么实现)
- 优化:我们可以使用切片集群中的不同实例来分别保存分布式锁和商品库存信息
#获取商品库存信息
local counts = redis.call("HMGET", KEYS[1], "total", "ordered"); #将总库存转换为数值
local total = tonumber(counts[1]) #将已被秒杀的库存转换为数值
local ordered = tonumber(counts[2]) #如果当前请求的库存量加上已被秒杀的库存量仍然小于总库存量,就可以更新库存
if ordered + k <= total then #更新已秒杀的库存量 redis.call("HINCRBY",KEYS[1],"ordered",k)
return k;
end
return 0
总结
-
秒杀场景有 2 个负载特征,分别是瞬时高并发请求和读多写少(为什么不利用消息队列消峰谷 是因为想要带给用户更好的体验嘛)\
-
秒杀前:前端 CDN 和浏览器缓存拦截大量秒杀前的请求
-
秒杀中:利用lua脚本或分布式锁完成原子操作
-
库存信息持久存储,并且保证淘汰策略不淘汰allkey\
\