必须在应用层为每个SET或SETEX操作的EX参数手动加随机偏移,Redis本身不提供自动打散能力;固定TTL会导致批量key同一秒集中过期,引发请求穿透压垮数据库,需用ThreadLocalRandom在±10%基础TTL内生成合理偏移并封装复用。

必须在应用层为每个 SET 或 SETEX 操作的 EX 参数手动加随机偏移,Redis 本身不提供自动打散能力。
为什么固定 TTL 是定时炸弹
当批量写入缓存(如定时任务刷新商品列表)时,若全部使用 redis.setex("product:1001", 3600, value) 这类固定过期时间,所有 key 就会在同一秒集体失效。下一波请求涌来,cache miss 瞬间变成数据库并发查询洪峰——不是 Redis 崩了,是数据库被“误杀”。
常见错误现象包括:redis_expired_keys_total 指标突增、DB 的 Threads_running 拉满、连接池 wait 超时率飙升。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 别信“开了
lazyfree-lazy-expire yes就安全”——它只异步删 key,不阻止请求穿透 - 别用
new Random(System.currentTimeMillis())初始化随机数生成器,多线程下极易重复 - 禁止在业务代码里裸写
Random.nextInt(360),必须封装复用
怎么加合理随机偏移(Java + RedisTemplate)
核心是控制扰动范围:太小不起作用,太大导致部分 key 寿命缩水严重,业务不可接受。
- 基础 TTL 为 3600 秒时,推荐偏移范围设为 ±360 秒(即 10%),调用
ThreadLocalRandom.current().nextInt(0, 720)后减去 360 得到 [-360, 360) 区间 - 封装成工具方法,强制业务走统一逻辑:
setWithJitter(key, value, baseSeconds, jitterSeconds) - 示例代码中不要写
Duration.ofSeconds(base + rand)后直接传入,需校验结果是否 > 0(避免负数 TTL 导致立即过期) - 如果用 Jedis,对应调用
jedis.set(key, value, SetParams.setParams().ex(actualSeconds)),别漏掉SetParams
随机值不是越大越好,得看业务容忍度
加扰动不是为了“看起来分散”,而是让失效压力在时间轴上真正摊薄。偏离业务实际需求反而引入新问题。
- 偏移超过基础 TTL 的 20%,比如 3600s 基础 + ±720s,会导致部分 key 实际存活仅 2880s —— 热点数据可能还没被访问就过期
- 对强一致性要求高的 key(如用户余额),不宜加过大偏移,可改用“逻辑过期”:value 中嵌入
expire_at字段,由应用判断并异步刷新 - 用
scan批量查ttl时,务必带COUNT 100参数,否则单次扫描阻塞 Redis 主线程,可能触发客户端重试链式雪崩
最容易被忽略的一点:随机扰动只解决“集中失效”,但挡不住 Redis 整体宕机。一旦 redis-cli ping 失败,所有 key 都等于已过期——这时候靠的是熔断、本地缓存和限流,不是 TTL 设置有多漂亮。

















