Node.js Redis 缓存实战:缓存读写与一致性处理

Redis 缓存让 Node.js 应用响应提速一个量级。本文讲解连接配置、API 与页面缓存实战、缓存键设计、三大缓存策略与缓存一致性方案。

最佳实践
应用接口与数据存储连接插画

Redis 缓存的核心模式是 Cache-Aside:先查缓存,未命中查数据库并回填,设 TTL 兜底过期。三个关键决策——缓存键设计(含版本号)、TTL 策略(基础值+随机抖动防雪崩)、更新时的失效策略(写后删缓存而非更新缓存)。

连接 Redis

npm install redis
const { createClient } = require('redis');

const redis = createClient({ url: process.env.REDIS_URL });
redis.on('error', (err) => console.error('Redis 错误:', err));
await redis.connect();

场景一:缓存 API 响应

async function getUser(id) {
  const cacheKey = `user:${id}`;

  // 1. 先查缓存
  const cached = await redis.get(cacheKey);
  if (cached) return JSON.parse(cached);

  // 2. 未命中查数据库
  const user = await db.users.findById(id);
  if (!user) return null;

  // 3. 回填缓存,TTL 300 秒
  await redis.setEx(cacheKey, 300, JSON.stringify(user));
  return user;
}

场景二:缓存整页响应

app.get('/api/popular-posts', async (req, res) => {
  const cached = await redis.get('posts:popular');
  if (cached) {
    res.setHeader('X-Cache', 'HIT');
    return res.json(JSON.parse(cached));
  }

  const posts = await db.getPopularPosts();
  await redis.setEx('posts:popular', 60, JSON.stringify(posts));
  res.setHeader('X-Cache', 'MISS');
  res.json(posts);
});

X-Cache 响应头是排障利器——联调时一眼看出缓存是否生效。

缓存键设计规范

  • 命名空间:业务:实体:标识(shop:product:123),可冒号分层;
  • 参数进键:列表缓存要含分页/筛选参数(posts:page:2:sort:hot);
  • 版本号:v2:user:123——数据结构升级时整体换代,旧键自然过期。

三大缓存策略

策略 模式 适用
Cache-Aside 读:先缓存后库;写:更新库+删缓存 绝大多数场景(本文模式)
Write-Through 写缓存与库同步完成 一致性要求高的读多写少
Write-Behind 先写缓存异步刷库 写密集、可容忍延迟

缓存一致性:实战要点

写后删缓存,而不是更新缓存:

async function updateUser(id, data) {
  await db.users.update(id, data);
  await redis.del(`user:${id}`);  // 删除而非更新——下次读时回填最新
}

更新缓存有并发乱序问题(两个写请求交错导致旧值覆盖新值);删除模式让"下次读"自然拿到最新值。TTL 是最后防线——即使失效逻辑漏了,数据也不会永远错。

防雪崩:大量键同时过期会打穿数据库——TTL 加随机抖动(300 + Math.random()*60)。

用命中率和回源延迟验证缓存收益

缓存加上后,接口变快还是偶尔更慢,需要同时看 Redis 命中与淘汰、应用回源次数和数据库调用耗时。观测云 Redis 采集器提供实例层指标;业务键空间的命中率通常需要应用单独计数。为命中率设阈值前先建立业务基线,避免把固定百分比当作所有服务的合格线;高基数键和值也不应直接作为监控标签。

接入文档:Redis 采集 · Node.js APM 接入 · 监控器与告警策略。

常见问题(FAQ)

Q:缓存穿透怎么办?(查询不存在的数据)

缓存空值(null 也缓存 30 秒)挡住恶意刷库;大流量场景用布隆过滤器前置拦截。

Q:多实例应用的本地缓存和 Redis 怎么选?

多实例部署必须 Redis(本地缓存各实例不一致);单实例小应用内存缓存(lru-cache)更轻。集群化的正确姿势见本系列《Node.js 集群扩容实战》。

Q:缓存和数据库事务一致性怎么保证?

先写数据库再删缓存仍存在并发回填旧值和删除失败窗口。TTL 只提供过期上限;需要重试、版本校验或失效事件等机制,严格一致性不能仅靠一把分布式锁保证。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台