Node.js 使用 Redis
Node.js与Redis天然契合,本课学习Node.js操作Redis。
Redis Node.js客户端
主流Node.js Redis客户端:
- ioredis:功能强大,支持集群、哨兵、Lua脚本
- redis(node-redis):官方客户端,简单易用
- redis(v3):旧版本,广泛使用
💡 推荐: 新项目使用ioredis或node-redis v4+。
ioredis
安装
BASH
npm install ioredis
基本连接
JAVASCRIPT
const Redis = require('ioredis');
// 连接Redis
const redis = new Redis({
host: 'localhost',
port: 6379,
// password: 'your_password',
db: 0
});
// 测试连接
redis.ping().then(result => {
console.log(result); // 'PONG'
});
字符串操作
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
// SET和GET
async function stringDemo() {
// SET
await redis.set('name', 'Alice');
// GET
const name = await redis.get('name');
console.log(name); // 'Alice'
// SET带过期时间
await redis.set('session', 'data', 'EX', 3600); // 1小时后过期
// MSET和MGET
await redis.mset('key1', 'value1', 'key2', 'value2');
const values = await redis.mget('key1', 'key2');
console.log(values); // ['value1', 'value2']
// INCR
await redis.set('counter', 0);
const count = await redis.incr('counter');
console.log(count); // 1
// DEL
await redis.del('name');
}
stringDemo();
哈希操作
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function hashDemo() {
// HSET和HGET
await redis.hset('user:1', 'name', 'Alice');
await redis.hset('user:1', 'age', 25);
const name = await redis.hget('user:1', 'name');
console.log(name); // 'Alice'
// HMSET
await redis.hmset('user:2', {
name: 'Bob',
age: 30,
city: 'Beijing'
});
// HGETALL
const user = await redis.hgetall('user:2');
console.log(user); // { name: 'Bob', age: '30', city: 'Beijing' }
// HKEYS和HVALS
const keys = await redis.hkeys('user:2');
const values = await redis.hvals('user:2');
// HDEL
await redis.hdel('user:2', 'city');
// HINCRBY
await redis.hincrby('user:2', 'age', 1);
}
hashDemo();
列表操作
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function listDemo() {
// LPUSH和RPUSH
await redis.lpush('mylist', 'value1', 'value2');
await redis.rpush('mylist', 'value3');
// LRANGE
const list = await redis.lrange('mylist', 0, -1);
console.log(list); // ['value2', 'value1', 'value3']
// LPOP和RPOP
const left = await redis.lpop('mylist');
const right = await redis.rpop('mylist');
// LLEN
const length = await redis.llen('mylist');
}
listDemo();
集合操作
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function setDemo() {
// SADD
await redis.sadd('myset', 'a', 'b', 'c');
// SMEMBERS
const members = await redis.smembers('myset');
console.log(members); // Set { 'a', 'b', 'c' }
// SISMEMBER
const exists = await redis.sismember('myset', 'a');
console.log(exists); // 1 (true)
// SREM
await redis.srem('myset', 'a');
// SINTER、SUNION、SDIFF
await redis.sadd('set1', 'a', 'b', 'c');
await redis.sadd('set2', 'b', 'c', 'd');
const inter = await redis.sinter('set1', 'set2');
console.log(inter); // Set { 'b', 'c' }
}
setDemo();
有序集合操作
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function zsetDemo() {
// ZADD
await redis.zadd('leaderboard', 100, 'player1', 200, 'player2', 150, 'player3');
// ZRANGE
const top = await redis.zrange('leaderboard', 0, -1, 'WITHSCORES');
console.log(top); // ['player1', '100', 'player3', '150', 'player2', '200']
// ZREVRANGE(从高到低)
const topDesc = await redis.zrevrange('leaderboard', 0, 2, 'WITHSCORES');
// ZSCORE
const score = await redis.zscore('leaderboard', 'player1');
console.log(score); // '100'
// ZINCRBY
await redis.zincrby('leaderboard', 50, 'player1');
// ZREM
await redis.zrem('leaderboard', 'player1');
}
zsetDemo();
发布订阅
JAVASCRIPT
const Redis = require('ioredis');
// 订阅者
const subscriber = new Redis();
subscriber.subscribe('news', 'sports');
subscriber.on('message', (channel, message) => {
console.log(`频道:${channel},消息:${message}`);
});
// 发布者
const publisher = new Redis();
setInterval(() => {
publisher.publish('news', `新闻消息 ${Date.now()}`);
}, 1000);
管道
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function pipelineDemo() {
const pipeline = redis.pipeline();
pipeline.set('key1', 'value1');
pipeline.set('key2', 'value2');
pipeline.set('key3', 'value3');
pipeline.get('key1');
const results = await pipeline.exec();
console.log(results);
// [ [ null, 'OK' ], [ null, 'OK' ], [ null, 'OK' ], [ null, 'value1' ] ]
}
pipelineDemo();
事务
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function transactionDemo() {
const multi = redis.multi();
multi.set('key1', 'value1');
multi.set('key2', 'value2');
multi.incr('counter');
const results = await multi.exec();
console.log(results);
}
transactionDemo();
连接池
ioredis内置连接池,无需额外配置:
JAVASCRIPT
const Redis = require('ioredis');
// 单个连接(自动连接池)
const redis = new Redis({
host: 'localhost',
port: 6379,
maxRetriesPerRequest: 3,
enableReadyCheck: true
});
// 集群模式
const cluster = new Redis.Cluster([
{ host: '127.0.0.1', port: 7000 },
{ host: '127.0.0.1', port: 7001 },
{ host: '127.0.0.1', port: 7002 }
]);
node-redis(官方客户端)
安装
BASH
npm install redis
基本使用
JAVASCRIPT
const { createClient } = require('redis');
async function main() {
// 创建客户端
const client = createClient({
url: 'redis://localhost:6379'
});
// 连接
await client.connect();
// SET和GET
await client.set('key', 'value');
const value = await client.get('key');
console.log(value); // 'value'
// 哈希操作
await client.hSet('user:1', 'name', 'Alice');
const name = await client.hGet('user:1', 'name');
// 列表操作
await client.lPush('mylist', 'value1');
const list = await client.lRange('mylist', 0, -1);
// 集合操作
await client.sAdd('myset', 'a', 'b', 'c');
const members = await client.sMembers('myset');
// 关闭连接
await client.quit();
}
main();
发布订阅
JAVASCRIPT
const { createClient } = require('redis');
async function pubsub() {
const subscriber = createClient();
await subscriber.connect();
// 订阅
await subscriber.subscribe('news', (message) => {
console.log(`收到消息:${message}`);
});
const publisher = createClient();
await publisher.connect();
// 发布
await publisher.publish('news', 'Hello World');
}
pubsub();
实战案例
案例1:缓存装饰器
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
function cache(expire = 3600) {
return function(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function(...args) {
const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;
// 尝试从缓存获取
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// 执行原方法
const result = await originalMethod.apply(this, args);
// 存入缓存
await redis.set(cacheKey, JSON.stringify(result), 'EX', expire);
return result;
};
return descriptor;
};
}
class UserService {
@cache(300)
async getUser(userId) {
console.log(`查询数据库:userId=${userId}`);
return { id: userId, name: `User${userId}` };
}
}
案例2:分布式锁
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
const { v4: uuidv4 } = require('uuid');
class DistributedLock {
constructor(key, expire = 10) {
this.key = `lock:${key}`;
this.expire = expire;
this.identifier = uuidv4();
}
async acquire() {
const result = await redis.set(
this.key,
this.identifier,
'NX',
'EX',
this.expire
);
return result === 'OK';
}
async release() {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
const result = await redis.eval(script, 1, this.key, this.identifier);
return result === 1;
}
}
// 使用锁
async function withLock(key, callback) {
const lock = new DistributedLock(key);
try {
while (!(await lock.acquire())) {
await new Promise(resolve => setTimeout(resolve, 100));
}
return await callback();
} finally {
await lock.release();
}
}
// 示例
await withLock('resource', async () => {
console.log('执行业务逻辑');
});
案例3:限流器
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function rateLimit(key, limit = 10, period = 60) {
const current = Math.floor(Date.now() / 1000);
const windowStart = current - period;
const results = await redis
.multi()
.zremrangebyscore(key, 0, windowStart)
.zcard(key)
.zadd(key, current, `${current}`)
.expire(key, period)
.exec();
const count = results[1][1];
return count < limit;
}
// 使用限流
async function apiHandler(userId) {
const allowed = await rateLimit(`api:${userId}`, 10, 60);
if (allowed) {
console.log('请求允许');
return { success: true };
} else {
console.log('请求拒绝');
return { success: false, error: 'Rate limit exceeded' };
}
}
案例4:消息队列
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
class MessageQueue {
constructor(name) {
this.name = `queue:${name}`;
}
async push(message) {
await redis.rpush(this.name, JSON.stringify(message));
}
async pop(timeout = 0) {
const result = await redis.blpop(this.name, timeout);
if (result) {
return JSON.parse(result[1]);
}
return null;
}
async size() {
return await redis.llen(this.name);
}
}
// 生产者
const queue = new MessageQueue('tasks');
await queue.push({ task: 'send_email', to: 'user@example.com' });
// 消费者
while (true) {
const task = await queue.pop(5);
if (task) {
console.log('处理任务:', task);
} else {
break;
}
}
错误处理
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
redis.on('error', (error) => {
console.error('Redis错误:', error);
});
redis.on('connect', () => {
console.log('Redis连接成功');
});
redis.on('ready', () => {
console.log('Redis准备就绪');
});
redis.on('close', () => {
console.log('Redis连接关闭');
});
❓ 常见问题
Q ioredis和node-redis如何选择?
A ioredis功能更强大(集群、哨兵),node-redis是官方客户端。推荐ioredis。
Q ioredis是线程安全的吗?
A 是的。ioredis内置连接池,可以在多个地方共享实例。
Q 如何处理连接断开?
A ioredis会自动重连。可以监听error和connect事件。
Q 异步操作如何处理?
A ioredis所有操作返回Promise,使用async/await或.then()。
Q 如何实现连接池?
A ioredis内置连接池,无需额外配置。
📖 小节
- ioredis:功能强大,支持集群、哨兵、Lua脚本
- node-redis:官方客户端,简单易用
- 所有操作异步,返回Promise
- 支持管道、事务、发布订阅
- 内置连接池,自动重连
- 实战案例:缓存、分布式锁、限流、消息队列
📝 作业
- 基本操作: 使用ioredis实现字符串、哈希、列表的基本操作
- 管道: 使用管道批量设置1000个键,对比性能
- 发布订阅: 实现简单的发布订阅消息系统
- 实战: 实现一个简单的分布式锁或限流器
恭喜完成!
你已经完成了Redis教程的全部26课学习!接下来可以:
- 实践:在实际项目中应用Redis
- 深入:学习Redis集群、哨兵、Lua脚本
- 优化:学习Redis性能优化和最佳实践



