单靠Redis集群扛不住秒杀峰值,因其远程调用在网络延迟、序列化和连接池争抢下性能骤降;需结合本地缓存(Caffeine+逻辑过期)、Nginx+Lua承接70%热点读,并避免简单注解式缓存导致的节点压力集中。

为什么单靠Redis集群扛不住秒杀峰值
因为Redis再快也是远程调用,万级QPS下网络延迟、序列化开销、连接池争抢会迅速放大。某次大促实测:当单节点Redis QPS突破8000,平均响应从0.8ms跳到4.2ms,超时请求开始堆积。更关键的是,大量重复读请求(比如同一商品被10万人同时刷详情页)本不该打到Redis——它们完全可以在更靠近用户的层就拦截掉。
本地缓存(Caffeine)必须配合逻辑过期控制
直接用 Caffeine 做纯内存缓存会引发一致性灾难:库存扣减后本地缓存没失效,用户看到的还是旧余量。正确做法是把过期时间“藏进value里”,而不是依赖 expire 指令:
-
CacheWrapper类必须包含expireTime字段和isExpired()方法 - 每次从Redis读取后,先校验
isExpired(),再决定是否回源或触发异步刷新 - 本地缓存的
expireAfterWrite只设为兜底值(比如30秒),不参与业务逻辑判断
否则会出现“库存显示还有100,点进去却提示售罄”的典型现象。
Nginx+Lua层要承担70%以上热点读流量
这不是可选项,是压测验证过的分水岭。OpenResty 的 redis.call() 调用 Redis 平均耗时
- 在
nginx.conf中启用lua_shared_dict做进程内共享缓存,避免每个 worker 重复查 Redis - Lua 脚本里必须做两级 fallback:先查
shared_dict→ 再查 Redis → 最后才透传给 upstream - 对
seckill_stock_{goodsId}这类 key,Nginx 层要主动执行DECR预减,失败才降级到应用层
漏掉第三条,等于把最核心的库存原子操作又交还给了应用层,多级缓存就失去意义。
热点Key隔离不能只靠Redis分片
Redis Cluster 对 HASH_SLOT 分布是静态的,TOP 100 商品的访问量可能占全部请求的60%,导致个别节点 CPU 100%。真实有效的隔离手段是:
- 为高热商品(如iPhone新品)单独部署一组 Redis 实例,不与其他业务混用
- 在应用层路由时,用
goodsId % 4 == 0这类硬规则强制打到指定实例,绕过 Cluster 自动分片 - 监控
redis-cli --stat输出的instantaneous_ops_per_sec,单实例持续 >15000 就要拆分
很多人以为加个 @Cacheable(key = "#goodsId") 就算做了热点隔离,其实只是把压力从数据库转嫁到了同一个Redis节点上。


















