当十万级QPS如洪水般涌来,Redis就是你手中那把劈开洪峰的利刃。
秒杀,堪称互联网架构中最残酷的压力测试——瞬时10万+ QPS、库存仅百件、99%的请求注定失败。传统数据库TPS仅5000左右,压测中100并发就能产生15.2%的超卖率。这不是系统,这是灾难。
秒杀面临三大核心难题:
难题 | 表现 | 后果 |
|---|---|---|
🌊 瞬时洪峰 | 1-3秒内汇聚数十万甚至上百万请求 | 服务熔断、数据库卡死、系统雪崩 |
🎯 并发竞争 | 多用户多实例同时扣减库存 | 超卖、少卖、库存残留 |
🔄 同步链路过长 | 请求→校验→扣减→写入全链路同步 | 响应超时、容错极差 |
结论只有一个:Redis是唯一能同时满足秒杀"高并发、低延迟、原子性、分布式"五大核心需求的中间件,没有之一。
特性 | 表现 | 实测数据 |
|---|---|---|
🚀 极致性能 | 内存存储 + 单线程事件驱动 + IO多路复用(epoll) | 单节点轻松支撑10万QPS,P99延迟微秒级 |
⚡ 原子操作 | Lua脚本批量命令原子执行 | 竞态超卖率归零 |
🧰 数据结构丰富 | String/Hash/Set/ZSet/List/Stream | 一套组件覆盖库存、防重、排队、限流全场景 |
🛡️ 高可用集群 | Cluster + Sentinel + 主从复制 | 故障秒级自动转移,服务不中断 |
Redis将所有数据存储在内存中,访问速度在纳秒级别,完全绕过了机械硬盘的寻道时间和旋转延迟。配合Linux epoll IO多路复用,单进程即可高效管理成千上万的客户端连接——这就是百万级并发的底气。
最基础的高可用方案。主节点处理写,从节点处理读,数据单向同步。
⚠️ 致命短板:主节点故障需人工切换,无法数据分片,写操作无法负载均衡。
在主从复制之上,引入独立Sentinel进程,实现无人值守的自动容灾。
生产配置三大铁律:
配置项 | 推荐值 | 作用 |
|---|---|---|
Sentinel节点数 | ≥3个 | 避免脑裂 |
min-slaves-to-write | 1 | 至少1个从节点同步成功才允许写 |
repl-ping-slave-period | 10秒 | 10秒内未收到回复则拒绝写 |
这才是秒杀系统的核心答案。 Redis 3.0+官方提供的去中心化分布式方案,彻底解决容量、性能、可用性三大痛点。
slot = CRC16(key) & 16383 // 固定公式,客户端本地计算要素 | 说明 |
|---|---|
槽位总数 | 16384个(0~16383),不是65536!Redis作者权衡:节点数<1000时16384够用,bitmap压缩率最优 |
数据分片 | 每个主节点负责一部分槽位,如3节点集群各负责约5461个槽 |
路由方式 | 客户端计算slot → 查槽位-节点映射 → 直接访问目标主节点 |
槽位迁移 | 扩容/缩容时在线迁移,客户端临时重定向,无缝扩展 |
为什么是16384而不是65536? 节点数200时,16384槽的心跳消息头仅8KB;若65536槽,心跳包过于庞大,200节点集群带宽直接飙升到100M+,得不偿失。
阶段 | 动作 | 时间 |
|---|---|---|
PFAIL | 节点15秒内未响应PING → 发送方标记为主观下线 | 15秒 |
FAIL | 集群多数主节点确认 → 标记为客观下线 | 即时 |
投票 | 故障主节点的从节点发起选举 → 获得多数票者晋升为新主 | 秒级 |
恢复 | 原主节点重启后自动降级为从节点 | 自动 |
javaString stock = jedis.get("stock");
if (stock > 0) {
jedis.decr("stock"); // 10个请求同时读到stock=1,全部通过,库存变-9!
}压测结果:100并发,超卖率15.2%。
javajedis.multi();
jedis.decr("stock");
jedis.sadd("users", uid);
jedis.exec(); // 乐观锁导致大量请求失败,库存仍有剩余(少卖)lua-- 防重复抢购 + 库存扣减 + 原子执行,一次网络往返搞定
local userid = KEYS[1]
local prodid = KEYS[2]
local qtkey = "sk:"..prodid..":qt"
local usrkey = "sk:"..prodid..":usr"
-- ① 防重复抢购
local exists = redis.call("sismember", usrkey, userid)
if tonumber(exists) == 1 then return 2 end
-- ② 库存不足
local num = redis.call("get", qtkey)
if tonumber(num) <= 0 then return 0 end
-- ③ 原子扣减 + 记录用户
redis.call("decr", qtkey)
redis.call("sadd", usrkey, userid)
return 1 -- 成功返回值 | 含义 |
|---|---|
0 | 库存不足,秒杀结束 |
1 | 秒杀成功 ✅ |
2 | 已抢购过,禁止重复 🚫 |
效果:网络消耗从3次往返降为1次,竞态超卖率归零。
用户请求
│
▼
┌─────────────────┐
│ ① 网关层限流 │ ← Nginx/Sentinel:IP限流、频率拦截、黑白名单
├─────────────────┤
│ ② 应用层防重 │ ← JVM本地缓存 + Redis全局防重,拦截重复抢购
├─────────────────┤
│ ③ Redis核心层 │ ← 库存预热、分布式锁、Lua原子扣减、队列削峰
├─────────────────┤
│ ④ 异步消息层 │ ← Redis Stream/MQ:解耦秒杀与数据库落地
├─────────────────┤
│ ⑤ 数据库落地层 │ ← 低速平稳执行订单创建、库存落地、数据持久化
└─────────────────┘核心思想:所有高并发、高压力操作全部下沉至Redis,数据库只负责平稳的最终数据落地。
方案 | 原子性 | 锁续约 | 可重入 | 生产推荐 |
|---|---|---|---|---|
SETNX+EXPIRE | ❌ 非原子 | ❌ | ❌ | ❌ 禁止使用 |
SET key value NX PX timeout | ✅ 原子 | ❌ | ❌ | ⚠️ 中小型项目 |
Lua脚本释放锁 | ✅ 原子 | ❌ | ❌ | ✅ 推荐 |
Redisson框架 | ✅ | ✅ 看门狗 | ✅ | ✅✅ 企业级首选 |
Redisson相比自定义实现,额外支持公平锁、锁降级、集群锁,是秒杀场景的最优解。
用户请求
→ 限流模块过滤(令牌桶:每秒1000请求)
→ Lua脚本原子执行(防重 + 查库存 + 扣减)
→ 成功 → 写入Redis Stream:XADD orders * user_id 123 product_id 1001
→ 消费者组异步批量写入MySQL
→ 定时任务清理超时未支付订单并恢复库存库存回滚机制:
lualocal restored = redis.call('INCR', 'product:1001:stock')
redis.call('SREM', 'product:1001:users', user_id)维度 | Redis表现 | 传统数据库 |
|---|---|---|
抗并发 | 10万QPS,微秒级延迟 | 5000 TPS,毫秒级延迟 |
原子性 | Lua脚本,竞态超卖率0% | 事务锁,超卖率15%+ |
高可用 | 秒级自动故障转移 | 主从切换分钟级 |
扩展性 | 16384槽位水平扩展 | 垂直扩展到顶 |
四个字:快、准、狠、稳。
Redis不是秒杀的可选项,而是必选项。掌握Redis Cluster + Lua原子脚本 + 五层削峰架构,你就掌握了扛住百万级洪峰的终极武器。
实战出真知,压测见真章。当流量洪峰来袭,愿你的系统如磐石般稳固,如闪电般迅捷。 ⚡
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。