1. Node.js与Redis连接基础
Redis作为高性能的内存数据库,在现代Web开发中扮演着重要角色。Node.js通过异步非阻塞I/O模型与Redis的快速内存操作完美契合,这种组合特别适合需要处理高并发、低延迟场景的应用。
1.1 环境准备
在开始连接前,确保已安装以下组件:
- Node.js 16.x或更高版本(建议使用LTS版本)
- Redis服务器5.0+版本
- npm或yarn包管理器
验证Node.js安装:
node -v npm -vRedis服务检查:
redis-cli ping # 应返回 PONG1.2 客户端库选型
Node.js生态中有多个Redis客户端库可供选择:
node-redis(官方推荐)
- 最新v4.x支持Promise API
- 完整的Redis命令支持
- 活跃的维护更新
ioredis(备选方案)
- 支持集群和哨兵模式
- 自动重连机制
- 兼容旧版API
提示:新项目建议直接使用node-redis,老项目迁移可参考官方迁移指南
2. 连接实现详解
2.1 基础连接配置
安装node-redis客户端:
npm install redis基础连接示例:
import { createClient } from 'redis'; const client = createClient({ url: 'redis://localhost:6379' }); client.on('error', (err) => { console.error('Redis Client Error', err); }); await client.connect();2.2 连接参数配置
常用连接配置项:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| socket | Object | - | 套接字配置 |
| socket.host | String | 'localhost' | Redis主机地址 |
| socket.port | Number | 6379 | Redis端口 |
| socket.tls | Boolean | false | 启用TLS加密 |
| password | String | null | 认证密码 |
| database | Number | 0 | 数据库索引 |
生产环境推荐配置:
const client = createClient({ socket: { host: 'production.redis.example.com', port: 6379, tls: true, reconnectStrategy: (retries) => Math.min(retries * 100, 5000) }, password: 'complex_password_123', database: 1 });2.3 连接状态管理
重要事件监听:
client.on('connect', () => { console.log('Connecting to Redis...'); }); client.on('ready', () => { console.log('Redis client ready'); }); client.on('end', () => { console.log('Connection closed'); }); client.on('reconnecting', () => { console.log('Attempting to reconnect...'); });连接健康检查:
// 检查底层socket状态 const isSocketOpen = client.isOpen; // 检查命令执行准备状态 const isReady = client.isReady;3. 核心操作实践
3.1 数据读写操作
字符串操作示例:
// 设置键值(自动序列化) await client.set('user:1000', JSON.stringify({ name: 'Alice', age: 28, lastLogin: new Date() })); // 获取并反序列化 const userData = JSON.parse(await client.get('user:1000'));哈希操作示例:
// 设置哈希字段 await client.hSet('product:500', { name: 'Smartphone', price: 599.99, stock: 50 }); // 获取全部哈希字段 const product = await client.hGetAll('product:500');3.2 批量操作与管道
管道技术提升性能:
const pipeline = client.multi(); pipeline.set('counter', 0); pipeline.incr('counter'); pipeline.get('counter'); const results = await pipeline.exec(); // results = ['OK', 1, '1']3.3 事务处理
原子事务示例:
await client.executeIsolated(async (isolatedClient) => { await isolatedClient.watch('balance'); const balance = parseInt(await isolatedClient.get('balance')); if (balance < 100) { await isolatedClient.unwatch(); return; } const multi = isolatedClient.multi(); multi.decrBy('balance', 100); multi.incrBy('savings', 100); await multi.exec(); });4. 高级应用场景
4.1 发布/订阅模式
发布端:
await client.publish('notifications', JSON.stringify({ type: 'system', message: 'Server maintenance scheduled' }));订阅端:
const subscriber = client.duplicate(); await subscriber.connect(); await subscriber.subscribe('notifications', (message) => { console.log('Received:', JSON.parse(message)); });4.2 实现分布式锁
可靠锁实现:
async function acquireLock(lockName, timeout = 5000) { const identifier = Math.random().toString(36).substring(2); const lockKey = `lock:${lockName}`; const acquired = await client.set(lockKey, identifier, { NX: true, PX: timeout }); if (!acquired) throw new Error('Lock acquisition failed'); return { release: async () => { const script = ` if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end `; await client.eval(script, { keys: [lockKey], arguments: [identifier] }); } }; }4.3 性能优化技巧
- 连接池配置:
const client = createClient({ socket: { connectTimeout: 5000, keepAlive: 30000 } });- 命令批处理:
const commands = [ ['set', 'key1', 'value1'], ['hset', 'hash1', 'field1', 'value1'], ['zadd', 'zset1', 1, 'member1'] ]; await client.multi(commands).exec();- Lua脚本优化:
const script = ` local count = redis.call('GET', KEYS[1]) if tonumber(count) > tonumber(ARGV[1]) then redis.call('SET', KEYS[1], ARGV[2]) return 1 end return 0 `; await client.eval(script, { keys: ['threshold'], arguments: ['100', '50'] });5. 生产环境实践
5.1 错误处理策略
健壮的错误处理机制:
async function safeRedisCall(fn) { try { return await fn(); } catch (err) { if (err.code === 'ECONNRESET') { console.warn('Connection reset, attempting reconnect...'); await client.connect(); return safeRedisCall(fn); } throw err; } } // 使用示例 await safeRedisCall(() => client.get('important-data'));5.2 监控与日志
关键指标监控:
client.on('command', (command) => { monitor.log(`Command: ${command}`); }); client.on('commandQueueLength', (length) => { metrics.gauge('redis.queue_length', length); });5.3 连接关闭管理
优雅关闭方案:
async function gracefulShutdown() { try { await client.quit(); console.log('Redis connection closed'); } catch (err) { console.error('Error closing Redis:', err); } finally { process.exit(0); } } process.on('SIGTERM', gracefulShutdown); process.on('SIGINT', gracefulShutdown);6. 常见问题排查
6.1 连接问题
常见错误及解决方案:
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| ECONNREFUSED | Redis服务未启动 | 检查redis-server进程 |
| ETIMEDOUT | 网络问题/防火墙 | 验证网络连通性 |
| NOAUTH | 密码错误 | 检查认证配置 |
| ERR unknown command | 命令拼写错误 | 验证命令语法 |
6.2 性能问题
性能优化检查清单:
- 避免大键值(超过1MB)
- 使用管道批量操作
- 合理设置过期时间
- 避免阻塞命令(如KEYS)
- 使用SCAN替代全量遍历
6.3 内存管理
内存优化技巧:
// 设置自动过期 await client.set('temp:session', 'data', { EX: 3600 // 1小时后过期 }); // 使用压缩 await client.set('large:data', compress(data), { EX: 86400 });在实际项目中,我发现合理设置连接超时和重试策略能显著提高系统稳定性。对于关键业务路径,建议实现降级方案,当Redis不可用时可以切换到备用存储或本地缓存。