·更新于 2026年10月26日· 1 次浏览 RedisNuxtNode.js缓存性能优化BUG解决

Redis 缓存一起过期,把博客首页打慢了:我用互斥重建、空值缓存和随机 TTL 挡住回源风暴

一次首页缓存同时失效,请求全打到 MongoDB。我用互斥重建、空值缓存和随机 TTL,把缓存击穿和雪崩的压力降下来。

Redis 缓存一起过期,把博客首页打慢了:我用互斥重建、空值缓存和随机 TTL 挡住回源风暴 封面
ZHOUYI

作者

ZHOUYI

那天首页接口突然变慢,监控看到 Redis 命中率掉到底,MongoDB 查询数飙升。排查后发现首页聚合缓存和热门文章缓存都设了固定 10 分钟,发布文章后同一批 key 几乎同时过期,前端请求瞬间全部回源。

先分清:击穿、雪崩

击穿更像某个热 key 过期后,大量请求同时重建;雪崩则是大量 key 同时过期或缓存服务抖动,导致数据库被集中打穿。我的场景两者都沾一点:首页热 key 是击穿,批量文章缓存同刻过期像小规模雪崩。

防波堤怎么搭

  1. 给 TTL 加随机偏移:比如基础 600 秒加 0~120 秒,避免同一批缓存集体到期。
  2. 重建缓存时加互斥锁:只让一个请求查库回写,其他请求走旧缓存或降级数据。
  3. 给空结果做短缓存:删除页、不存在的 slug 也写入占位值,避免反复穿透数据库。

关键代码

js
const ttl = 600 + Math.floor(Math.random() * 120);
const cache = await redis.get(key);
if (cache !== null) return JSON.parse(cache);
const locked = await redis.set(`${key}:lock`, '1', 'NX', 'PX', 3000);
if (!locked) return getHomeFallback();
try {
  const data = await buildHomeData();
  await redis.set(key, JSON.stringify(data), 'EX', ttl);
  return data;
} finally {
  await redis.del(`${key}:lock`);
}

这里最容易踩坑的是锁过期和无限等待。锁的 PX 要比慢查询略长,但不要无限等待;拿不到锁时可以返回旧缓存、降级数据或稍后重试。在 Nuxt 的 useAsyncData 里也一样,服务端渲染阶段不要把每个请求都当成首次回源。首页这种读多写少页面,宁可短暂展示几十秒前的数据,也不要让所有请求都冲进 MongoDB。

TTL 和空值怎么定

有些文章被删除或 slug 不存在,如果只查库不缓存空结果,会被持续穿透。我会把空结果写成短 TTL 的占位值,比如 30~60 秒;如果内容频繁变化,再结合发布事件主动删除相关 key。注意 JSON 序列化时区分“空值占位”和“真的空字符串”,避免读取时误判。

随机 TTL 也不是越大越好。我最后把首页聚合缓存设成基础 10 分钟、随机 2 分钟,文章详情按内容更新频率分层。TTL 太长会让发布后更新变慢,太短又失去缓存意义,最好结合页面可接受的延迟来定。

“缓存不是“设个 TTL”就完事,过期策略、重建路径和失败降级要一起设计。”

Guestbook

留言

若这篇文章也曾停在你心上,留一行字吧,我会看的。

先确认一下邮箱

多这一步,是为了少些机器骚扰,也让我能更踏实地读每一条真人写下的字。验证码 10 分钟内有效;同一邮箱 60 秒内只能发一封。

本文留言

共 0 条已加载 0 条

这里还静悄悄的——愿意做第一个留下脚印的人吗?