大Key过期更危险,因其在惰性或主动删除时会阻塞主线程数十至数百毫秒,若叠加热点访问将引发“爆破式击穿”;普通Key删除仅微秒级,而大Key需遍历、释放内存及合并碎片,UNLINK才是异步删除关键开关。

大Key过期为什么比普通Key更危险
因为Redis单线程处理过期key是惰性+定期双策略,但大Key(比如一个hash含2万条商品SKU、或一个string存了8MB商品详情)在真正被访问触发惰性删除,或被周期扫描到执行主动删除时,会卡住主线程几十毫秒甚至上百毫秒。这时如果它恰好是热点key,大量请求在那一瞬间全量穿透——不是“击穿”,是“爆破式击穿”。
- 普通key过期只是标记+释放小内存,耗时微秒级;大Key过期要遍历、释放、合并内存碎片,可能阻塞整个Redis事件循环
- 监控里看不到明显QPS下跌,但
latency doctor会报出尖峰延迟,INFO commandstats里del或unlink调用耗时飙升 - 很多团队误以为加了互斥锁就能防住,结果锁没抢上,Redis自己先卡死了,锁根本发不出去
分拆大Key必须按访问粒度切,不能随便哈希打散
把一个hash:product:10086(含5000个field)强行按field名hash % 4拆成4个子hash,看似均匀,实际埋雷:如果前端只查price和stock两个field,却要遍历全部4个分片才能凑齐,IO翻4倍,反而更慢。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 优先按业务读写模式分:比如电商场景中,“基础信息”(name、desc)、“价格库存”(price、stock、sku_list)、“营销信息”(coupon、flash_start)各自独立成key,用
pipeline批量读取 - 集合类大Key(如
zset:user:123:feed存10万条动态)必须按时间窗口切,例如zset:user:123:feed:202605、zset:user:123:feed:202604,查最新动态只读当月key - 绝对不要对
string大Value做base64切片——解码拼接成本高,且无法局部更新;改用hash或stream结构存储可寻址字段
异步过期得靠UNLINK + 后台线程,不是单纯设TTL
EXPIRE只是打标记,真正删还是主线程干;UNLINK才把删除动作移交后台线程,这才是大Key过期不卡的底层开关。但光用UNLINK不够,得配合主动触发时机。
- 写入大Key时,不用
SET key value EX 3600,改用SET key value+ 单独EXPIRE key 3600,这样后续能用UNLINK key安全清理 - 在业务低峰期(比如凌晨2点),用
SCAN扫出所有待过期的大Key,逐个UNLINK,避免集中到期日爆发 - Spring Data Redis的
redisTemplate.delete()默认调DEL,必须显式用redisTemplate.unlink()(4.3+版本支持) - 注意:
UNLINK不保证立即释放内存,它只是把key从数据库字典移走扔给bio线程;观察INFO memory里的mem_not_counted_for_evict指标是否持续上涨,涨太多说明bio线程积压
最容易被忽略的点:分拆后的一致性校验和冷热分离
分拆本身不解决击穿,只是把“一个巨无霸崩掉”变成“多个小兵轮流扛压”。但如果分片之间更新不同步,或者冷数据还占着热key名,问题照旧。
- 每个分片key必须带独立TTL,比如
hash:prod:10086:base设24h,hash:prod:10086:price设5m——价格变频繁,基础信息变少,混设TTL等于白拆 - 上线前跑一致性脚本:用
HGETALL对比原大Key和各分片拼接结果,防止字段漏拆或重复 - 把历史归档数据(如3个月前的订单列表)迁到
redis-bloom或冷备集群,别让它和实时热点共用一个实例,否则eviction策略一触发,热key跟着被踢

















