热点商品缓存分片,治理思路就是把一个“超级流量入口”拆成多个“小流量入口”,让每个缓存节点都分担一部分读写压力,单点过载这个老问题就失去了存在的基础。常见的秒杀场景里,一个爆款SKU的缓存 key 会瞬间收到几十万次请求,如果不做分片,Redis 单节点 CPU 率先打满,紧接着超时、连接拒绝、缓存雪崩连锁发生,分片不是锦上添花,而是大促系统的保命动作。
单点过载到底发生在哪个环节
要理解分片的价值,先得看清热点key是怎么把系统拖垮的,假设某个电商平台在零点开启限量发售,商品详情接口先查缓存,缓存里存着这个商品的库存、价格、图文信息,所有用户请求同一个 key,比如product:100861,这个 key 被分配到 Redis 集群的某一个 slot 上,物理上落在一个主节点。
流量飙升后,这个节点的 CPU 使用率在几秒内冲到接近满负荷,Redis 是单线程模型,CPU 满载意味着该节点上的所有读写操作都排队等待,而这个节点上不只存放热点商品,还承载着其他业务的缓存数据,其他非热点请求也被殃及,出现大面积超时,此时应用层的连接池被打满,新请求全部阻塞在获取连接阶段,服务端线程资源耗尽,雪崩开始。
业内专家指出,单点过载的本质是流量没有按照预期分散,而是被集中引流到了固定节点,集群规模的扩大并不能解决这个问题,因为热点始终集中在同一个 slot 上。
热点商品缓存分片怎么设计才不踩坑
分片设计的目标很明确:让同一个热点商品的读取流量分散到多个节点,同时保证数据一致性和过期逻辑可控,以下三种策略可以组合使用,视业务紧急程度和改造成本选择。
热key加随机后缀拆分
这是最简单直接的方案,原本的 key 是product:100861,拆分成product:100861:01到product:100861:10十个子key,每个子key通过哈希算法落到不同的 slot,Redis 集群自动把这些 slot 分布到各个节点,写入时把同一份商品数据写入全部10个子key,读取时随机取一个子key。
这里有三个细节必须处理好:子key的TTL要一致,避免部分子key提前过期导致缓存穿透;失效逻辑要从“删一个key”变成“删一批key”,否则会出现同一个用户两次请求拿到不同数据;子key数量不宜过大,通常5到20个就能满足大促场景,过多会增加写入放大,每次商品信息变更要同步写几十个key,得不偿失。
本地缓存兜底
如果热点key的流量已经大到 Redis 集群所有节点都快扛不住,就需要在应用进程内做一级缓存,将热点商品数据缓存到 JVM 本地内存或进程内Caffeine缓存中,设置极短的过期时间,比如2到5秒,请求先查本地缓存,命中则直接返回,只有未命中才请求 Redis。
这个策略在秒杀场景下效果极好,因为热点商品在秒级内的数据变化不大,即使有2秒的延迟,对用户体验几乎无感知,改造时需要注意本地缓存的容量控制和逐出策略,避免大促期间每个应用实例都存一份热点数据,导致 JVM 内存吃紧,通常的做法是只对识别出的Top 20热点商品开启本地缓存,普通商品走原逻辑。
一致性哈希分片
客户端直连 Redis 的场景下,可以考虑用一致性哈希做分片,ShardedJedis、Redisson等客户端都内置了一致性哈希路由,key通过CRC16计算出哈希值后映射到环上的节点,相比 Redis Cluster 的固定slot机制,一致性哈希的优点是扩容时只需要迁移少量key,热点key可以通过虚拟节点机制进一步打散。
不过一致性哈希无法解决“同一个key只能在固定节点上”的问题,热点商品毕竟是同一个key,哈希到哪个节点,流量就打到哪个节点。因此一致性哈希一般搭配热key拆分使用,先将一个热key拆成多个子key,再通过一致性哈希天然分散到后端节点,这样才能实现真正的流量均匀分布。
秒杀场景缓存分片方案对比
实际生产环境中,团队面临的抉择通常是选哪种分片架构,行业共识认为,没有万能方案,只有适合当前集群规模和运维能力的方案,下表对比了四种常见做法:
| 方案 | 分片粒度 | 扩容方式 | 热点key处理 | 运维成本 |
|---|---|---|---|---|
| Redis Cluster原生分片 | 固定slot(16384个) | 在线迁移slot,需reshard | 需配合热key拆分 | 运维工具成熟,但迁移期间有延迟抖动 |
| 客户端一致性哈希 | 虚拟节点 | 加节点后部分key重新映射 | 天然分散,但仍需拆key | 无中心化组件,客户端升级麻烦 |
| Proxy代理模式(Codis/Twemproxy) | 按key哈希到组 | 加组并迁移数据 | 对客户端透明,Proxy层可做本地缓存 | 多一层Proxy,增加一次网络跳转 |
| 多级缓存(本地+Redis) | 不涉及分片 | 应用扩容即可 | 直接在应用层拦截绝大部分热点流量 | 实现简单,但需要考虑缓存一致性和内存开销 |
从实际落地的角度,带本地缓存的多级缓存方案是解决“超热key”的最优解,因为它彻底绕开了 Redis 节点瓶颈,Redis Cluster则是通用场景下的基础底座,适合绝大多数常规流量,Proxy模式适合已有 Codis 存量的团队,平滑过渡到集群方案。
落地分片改造的四步走
光知道方案还不够,怎么平稳落地才是关键,下面按照改造路径,给出可执行的操作步骤。
Redis热点key集群扩容步骤
第一步:清理内存碎片,确认集群健康。执行redis-cli --cluster check检查所有主节点和从节点的连接状态,确保没有fail节点,用INFO memory查看各节点已用内存,预留至少30%的扩容缓冲区,否则迁移时容易触发内存淘汰。
第二步:确定槽位迁移计划。如果已有集群的槽位分布倾斜,比如某个主节点承担了超过50%的槽位,需要先做一次redis-cli --cluster rebalance,让槽位均匀分布,注意rebalance会触发数据迁移,建议在流量低峰期执行,并设置migrate-timeout避免迁移超时。
第三步:添加新节点并迁移槽位。新节点启动后,用redis-cli --cluster add-node加入集群,然后用redis-cli --cluster reshard指定迁移的槽位数和目标节点ID,迁移过程中,源节点保持可读写,性能会有轻微下降,这是Redis Cluster的固有限制。
第四步:验证热点key是否已完成分散。用redis-cli --cluster keyslot product:100861:01查出子key的槽位号,确认多个子key落在了不同节点上,再用redis-cli --cluster info查看各节点key数量和内存,验证分布均匀。
电商大促缓存分片改造路径
改造不宜一蹴而就,建议在距离大促8周前启动,分四个阶段推进。
第一阶段:埋点识别热点key。 在Redis客户端Proxy层或应用拦截器里统计key的访问频次,输出Top 100热点key清单,有条件的团队可以使用redis-cli --hotkeys(需要开启maxmemory-policy为LFU)来定期扫描热点key,这一步为拆key提供数据依据。
第二阶段:执行“预热+拆key”双写。 预热脚本在业务低峰期运行,提前把热点商品数据写入全部子key,双写的含义是原始key和子key同时存在,读逻辑优先随机读子key,子key未命中则回源读原始key并触发预热逻辑,这个阶段灰度一部分流量,观察命中率和错误率。
第三阶段:限流降级兜底。 无论分片多么完善,尾部风险始终存在,在Redis前方加一层限流组件,比如Sentinel或自研网关,对热点key的并发访问量做限制,超出部分的请求直接返回降级数据(如缓存中的过期版本),同时准备一个开关,一旦分片集群出现异常,可以一键切回“本地缓存+直连DB”的极端模式。
第四阶段:全量切流并复盘。 确认分片集群稳定运行3天后,逐步切流量到新架构,切流过程中持续监控Redis节点CPU、内存、网络IO三项指标,任何一项超过70%就要检查槽位分布和子key数量是否合理,大促结束后,对比分片前后的响应延迟和错误率,形成复盘报告。
分片不是终点,容量规划才是
缓存分片解决了“流量打到一个点上”的架构问题,但容量规划决定了未来半年要不要再折腾一次,电商业务有个特点:大促峰值流量可能是平峰的10倍以上,而热点商品的流量又远超普通商品,做分片方案时,要按峰值流量的1.5倍来设计节点数量和子key数量,预留足够的缓冲空间。
另一个容易忽略的点是持久化和备份,分片后数据分布到了多个节点,单节点故障的影响面缩小了,但备份策略也要跟着调整,每个主节点都要配置从节点,且从节点的槽位分布要和主节点完全一致,否则故障切换后路由会失效。
热点商品缓存分片相关问答
热点商品缓存分片后出现数据不一致怎么办?
子key拆分必然带来数据更新时的写放大,推荐的做法是采用短TTL方案,把所有子key的过期时间统一设置为5到10秒,同时将更新操作改为“删除全部子key”,而不是“重新写入全部子key”,这样即使某一轮更新漏掉了部分子key,它们在几秒内也会自动过期,应用层回源数据库拿到最新数据,不一致的时间窗口被控制在秒级以内。
小规模电商系统有必要做热点商品缓存分片吗?
取决于单key的峰值QPS,如果秒杀时某商品key的请求量超过每秒1万次,就要考虑分片了,因为单个Redis节点即便用上最快的硬件,也扛不住这个量级,如果峰值在每秒2000次以下,先用本地缓存兜底就足够,强行分片反而增加运维复杂度,分片的启动条件不是业务规模大小,而是热点key的集中度。
分片和集群扩容有什么区别?
扩容是增加节点数量来提升总容量和吞吐量,但热点key不会因为集群多了几个节点就自动分散,分片是在key层面做拆分,把一个逻辑key变成多个物理key,让它们分布到不同节点,集群扩容解决的是“容量不够”的问题,分片解决的是“流量太集中”的问题,两者可以同时使用,互相补充。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/773015.html


