装
npm install ioredis
ioredis 是 Node 上最流行 + 最强的 Redis 客户端。
基础
import Redis from 'ioredis';
const redis = new Redis({
host: 'localhost',
port: 6379,
password: process.env.REDIS_PASSWORD,
});
// 或用 URL
const redis2 = new Redis('redis://default:password@localhost:6379');
await redis.set('user:42', JSON.stringify({ name: 'Alice' }));
const user = JSON.parse(await redis.get('user:42'));
await redis.quit();
String 操作
await redis.set('key', 'value');
await redis.set('key', 'value', 'EX', 60); // 60 秒后过期
await redis.set('key', 'value', 'EX', 60, 'NX'); // 不存在才设(锁)
await redis.get('key'); // 'value' 或 null
await redis.del('key');
await redis.exists('key'); // 1 / 0
await redis.incr('counter'); // 自增
await redis.incrby('counter', 10); // 加 10
await redis.expire('key', 60); // 设过期
await redis.ttl('key'); // 剩余秒数
Hash
await redis.hset('user:42', 'name', 'Alice');
await redis.hset('user:42', { name: 'Alice', age: 30, email: 'a@x' });
await redis.hget('user:42', 'name');
await redis.hgetall('user:42'); // 返回对象
await redis.hdel('user:42', 'email');
await redis.hincrby('user:42', 'age', 1);
List(队列)
await redis.rpush('queue', 'task1'); // 入队(尾)
await redis.lpush('queue', 'task1'); // 入队(头)
const task = await redis.lpop('queue'); // 出队(头)
const task2 = await redis.rpop('queue'); // 出队(尾)
// 阻塞式(队列消费者用)
const [_, task3] = await redis.blpop('queue', 10); // 等 10 秒
Set / Sorted Set
// Set
await redis.sadd('online', 'user42');
await redis.smembers('online');
await redis.sismember('online', 'user42');
// Sorted Set(带分数 / 用于排行榜)
await redis.zadd('leaderboard', 100, 'alice');
await redis.zadd('leaderboard', 200, 'bob');
await redis.zrevrange('leaderboard', 0, 9, 'WITHSCORES'); // top 10
Pub / Sub
// 订阅
const sub = new Redis();
await sub.subscribe('news');
sub.on('message', (channel, msg) => {
console.log(`[${channel}]`, msg);
});
// 发布(另一个连接)
const pub = new Redis();
await pub.publish('news', 'hello world');
⚠ 订阅连接不能再做普通命令——必须用单独 Redis 实例。
缓存模式(最常见用法)
async function getUser(id) {
const cacheKey = `user:${id}`;
// 1. 试缓存
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
// 2. 查 DB
const user = await db.users.findById(id);
if (!user) return null;
// 3. 写缓存(5 分钟过期)
await redis.set(cacheKey, JSON.stringify(user), 'EX', 300);
return user;
}
// 改动时清缓存
async function updateUser(id, data) {
await db.users.update(id, data);
await redis.del(`user:${id}`);
}
分布式锁
async function withLock(key, ttl, fn) {
const lockKey = `lock:${key}`;
const lockValue = Math.random().toString();
// 设锁(NX = 不存在才设)
const acquired = await redis.set(lockKey, lockValue, 'EX', ttl, 'NX');
if (!acquired) throw new Error('Failed to acquire lock');
try {
return await fn();
} finally {
// 用 Lua 脚本原子检查 + 删
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
await redis.eval(script, 1, lockKey, lockValue);
}
}
或用 Redlock 库。
限流
async function checkRateLimit(userId, limit, windowSec) {
const key = `ratelimit:${userId}`;
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, windowSec);
}
return count <= limit;
}
if (!await checkRateLimit('user42', 100, 60)) {
throw new Error('Too many requests');
}
Pipeline(批量减少 RTT)
const pipeline = redis.pipeline();
pipeline.set('a', 1);
pipeline.set('b', 2);
pipeline.set('c', 3);
const results = await pipeline.exec();
// 一次 RTT 完成 3 个操作
看连接情况
redis.on('connect', () => console.log('Connected'));
redis.on('ready', () => console.log('Ready'));
redis.on('error', err => console.error(err));
redis.on('close', () => console.log('Closed'));
redis.on('reconnecting', () => console.log('Reconnecting'));
坑
- 缓存 key 设计要有命名空间(如
user:42)—— 防止冲突 - 大 value(> 1 MB)放 Redis 慢——考虑放对象存储
- 不要把 Redis 当数据库——重要数据 + 持久化要双保险
- 过期时间设合理——太短 = 频繁查 DB;太长 = 数据陈旧
- 多实例时用 cluster 或 sentinel——单点风险
下一篇:ORM。