首页 / Node.js 教程 / Redis 缓存入门

Node.js 教程

Redis 缓存入门

本教程共 76 篇 · 第 54 篇 · 更新于 2026-07-25 · 约 4 分钟阅读

Node.jsRedis缓存会话数据结构

54. Redis 缓存入门

本节目标:Redis 常用数据结构,用它做缓存和会话存储。

数据库再快,也扛不住每秒几万次的重复查询。缓存的作用就是把热点数据放到更快的介质里,减轻数据库压力。Redis 是内存型键值数据库,读写性能在微秒级,是 Node.js 应用最常用的缓存方案。

安装与连接

Docker 起 Redis:

docker run -d -p 6379:6379 redis:7-alpine

Node.js 客户端用 ioredis

npm install ioredis
import Redis from 'ioredis';

const redis = new Redis({
  host: 'localhost',
  port: 6379,
  retryStrategy: (times) => Math.min(times * 50, 2000)
});

redis.on('error', (err) => console.error('Redis error', err));

retryStrategy 让连接断开后自动重连,生产环境必备。

基础数据结构

Redis 不只是「键值对」,值可以是多种结构:

String(字符串)

最常用,存 Token、计数器、序列化后的 JSON:

await redis.set('user:1:name', 'Alice');
await redis.set('user:1:profile', JSON.stringify({ age: 28 }), 'EX', 3600);

const name = await redis.get('user:1:name');
const profile = JSON.parse(await redis.get('user:1:profile'));

EX 3600 表示 1 小时后过期。缓存一定要设过期时间,否则内存迟早被打满。

Hash(哈希)

存对象属性,适合替代那种「把对象 JSON 化后整个塞进去」的做法:

await redis.hset('user:1', 'name', 'Alice', 'age', '28');
const user = await redis.hgetall('user:1');
// { name: 'Alice', age: '28' }

List(列表)

简单的消息队列、时间线:

await redis.lpush('news:feed', '文章A', '文章B');
const latest = await redis.lrange('news:feed', 0, 9); // 取前 10 条

Set(集合)

去重、判断存在性:

await redis.sadd('tags:nodejs', 'javascript', 'backend', 'server');
const isMember = await redis.sismember('tags:nodejs', 'backend'); // 1

Sorted Set(有序集合)

排行榜、优先级队列:

await redis.zadd('rank:score', 100, 'player1', 95, 'player2');
const top = await redis.zrevrange('rank:score', 0, 4, 'WITHSCORES');

缓存模式

Cache-Aside(旁路缓存)

最常用的模式:应用先查缓存,命中就返回;没命中查数据库,写入缓存,再返回。

async function getUser(id) {
  const cacheKey = `user:${id}`;
  let data = await redis.get(cacheKey);

  if (data) {
    return JSON.parse(data);
  }

  data = await db.query('SELECT * FROM users WHERE id = ?', [id]);
  if (data) {
    await redis.set(cacheKey, JSON.stringify(data), 'EX', 300);
  }
  return data;
}

缓存更新策略

策略说明适用场景
先更新数据库,再删缓存简单可靠,推荐大多数写场景
先更新数据库,再更新缓存可能写脏数据读多写少、允许短暂不一致
写直达(Write Through)同步写数据库和缓存强一致性要求

推荐「先更新数据库,再删缓存」的原因是:删除操作是幂等的,即使删失败,下次读也会从数据库回填,不会长期不一致。

缓存穿透、击穿、雪崩

这三个词面试常考,实际开发也容易踩:

缓存穿透:查询一个数据库和缓存里都没有的数据,每次都打到数据库。

解决:布隆过滤器,或者缓存空值(set(key, 'null', 'EX', 60))。

缓存击穿:热点 key 突然过期,大量请求同时打到数据库。

解决:热点 key 永不过期(逻辑过期),或者加互斥锁,只允许一个线程去重建缓存。

缓存雪崩:大量 key 同时过期,数据库压力陡增。

解决:过期时间加随机偏移,比如 EX 300 + Math.random() * 60

连接池与集群

单机 Redis 性能足够大部分场景。如果需要更高可用,用 Redis Cluster:

const redis = new Redis.Cluster([
  { host: '192.168.1.1', port: 6379 },
  { host: '192.168.1.2', port: 6379 }
]);