Redis 介绍与 Node.js 使用教程

Redis 介绍与 Node.js 使用教程

    正在检查是否收录...

Redis 介绍与 Node.js 使用教程

📚 目录

  1. Redis 简介
  2. 环境准备与快速开始
  3. 基础连接与配置
  4. 数据类型详解与示例
  5. 实际应用场景详解
  6. Express + Redis 完整实战
  7. 最佳实践与优化
  8. 常见问题与解决方案

Redis 简介

什么是 Redis?

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息代理。

主要特点

  • 内存存储

    :数据存储在内存中,读写速度极快
  • 数据持久化

    :支持 RDB 和 AOF 两种持久化方式
  • 丰富的数据类型

    :字符串、哈希、列表、集合、有序集合等
  • 高可用性

    :支持主从复制、哨兵模式、集群模式
  • 原子操作

    :所有操作都是原子性的

Redis 数据类型概览

1. 字符串 (String)

SET name "张三" GET name INCR counter EXPIRE session:123 3600 

2. 哈希 (Hash)

HSET user:1 name "李四" age 25 city "北京" HGET user:1 name HGETALL user:1 

3. 列表 (List)

LPUSH tasks "任务1" "任务2" RPOP tasks LRANGE tasks 0 -1 

4. 集合 (Set)

SADD fruits "苹果" "香蕉" "橘子" SMEMBERS fruits SISMEMBER fruits "苹果" 

5. 有序集合 (Sorted Set)

ZADD scores 85 "张三" 92 "李四" 78 "王五" ZRANGE scores 0 -1 WITHSCORES ZRANK scores "李四" 

环境准备与快速开始

环境要求

  • Node.js 16+
  • Redis 服务器
  • 基础 JavaScript 知识

服务器安装

这里采用docker安装

  1. 先下载dockerDesktop https://www.docker.com/products.../docker-desktop/

image.png

  1. 安装redis-server

image.png
3. 运行redis

image.png

image.png

image.png

redis 已经运行了,docker 安装的redis 没有密码,这里方便学习,就用docker安装,适合window

安装与启动

# 1. 安装依赖 npm install redis express 

基础连接与配置

基础连接示例 (基于 index.js)

import { createClient } from 'redis'; // 创建 Redis 客户端 const client = createClient({ socket: { host: 'localhost', port: 6379 }, password: '', // 如果设置了密码 database: 0 // 默认数据库 }); // 事件监听 client.on('error', (err) => { console.error('Redis 连接错误:', err); }); client.on('connect', () => { console.log('Redis 连接成功'); }); client.on('ready', () => { console.log('Redis 准备就绪'); }); client.on('end', () => { console.log('Redis 连接关闭'); }); // 连接到 Redis await client.connect(); // 使用完毕后断开连接 // await client.disconnect(); 

连接配置选项

const client = createClient({ socket: { host: 'localhost', // Redis 服务器地址 port: 6379, // Redis 端口 connectTimeout: 10000, // 连接超时时间 lazyConnect: true // 延迟连接 }, password: 'your_password', // Redis 密码 database: 0, // 数据库编号 retryDelayOnFailover: 100, // 故障转移重试延迟 maxRetriesPerRequest: 3 // 每个请求最大重试次数 }); 

数据类型详解与示例

1. 字符串 (String) - 缓存与计数器

用途

:缓存、计数器、会话存储、简单键值对

// 基本操作 await client.set('name', '张三'); await client.set('age', '20'); const name = await client.get('name'); console.log('姓名:', name); // 张三 // 设置过期时间 await client.setEx('session:123', 3600, 'session_data'); // 数字操作 await client.set('counter', 0); await client.incr('counter'); // 自增1 await client.incrBy('counter', 5); // 增加5 const counter = await client.get('counter'); console.log('计数器:', counter); // 6 // 批量操作 await client.mSet({ 'user:1:name': '张三', 'user:1:age': '25', 'user:1:city': '北京' }); const userData = await client.mGet(['user:1:name', 'user:1:age', 'user:1:city']); console.log('用户数据:', userData); // ['张三', '25', '北京'] // 检查键是否存在 const exists = await client.exists('name'); console.log('name 键存在:', exists); // 1 // 删除键 await client.del('name'); 

2. 哈希 (Hash) - 对象存储

用途

:对象属性存储、用户信息、配置管理

// 设置哈希字段 await client.hSet('user:1', { name: '李四', age: 25, city: '北京', email: 'lisi@example.com' }); // 获取单个字段 const userName = await client.hGet('user:1', 'name'); console.log('用户名:', userName); // 李四 // 获取所有字段 const user = await client.hGetAll('user:1'); console.log('用户信息:', user); // { name: '李四', age: '25', city: '北京', email: 'lisi@example.com' } // 获取多个字段 const fields = await client.hmGet('user:1', ['name', 'age']); console.log('姓名和年龄:', fields); // ['李四', '25'] // 检查字段是否存在 const hasEmail = await client.hExists('user:1', 'email'); console.log('有邮箱字段:', hasEmail); // 1 // 删除字段 await client.hDel('user:1', 'email'); // 获取字段数量 const fieldCount = await client.hLen('user:1'); console.log('字段数量:', fieldCount); // 3 

3. 列表 (List) - 队列与时间线

用途

:消息队列、任务队列、时间线、栈

// 从左侧添加元素 await client.lPush('tasks', '任务1', '任务2', '任务3'); // 从右侧添加元素 await client.rPush('tasks', '任务4'); // 获取列表长度 const length = await client.lLen('tasks'); console.log('任务数量:', length); // 4 // 获取范围内的元素 const allTasks = await client.lRange('tasks', 0, -1); console.log('所有任务:', allTasks); // ['任务3', '任务2', '任务1', '任务4'] // 从左侧弹出元素 (FIFO 队列) const leftTask = await client.lPop('tasks'); console.log('左侧弹出:', leftTask); // 任务3 // 从右侧弹出元素 (LIFO 栈) const rightTask = await client.rPop('tasks'); console.log('右侧弹出:', rightTask); // 任务4 // 阻塞式弹出 (消息队列) const message = await client.brPop('message_queue', 5); // 5秒超时 if (message) { console.log('收到消息:', message.element); } // 插入元素 await client.lInsert('tasks', 'BEFORE', '任务2', '新任务'); 

4. 集合 (Set) - 标签与去重

用途

:标签系统、去重、集合运算、好友关系

// 添加元素到集合 await client.sAdd('fruits', '苹果', '香蕉', '橘子'); await client.sAdd('vegetables', '白菜', '萝卜', '苹果'); // 注意苹果重复 // 获取集合所有成员 const fruits = await client.sMembers('fruits'); console.log('水果:', fruits); // ['苹果', '香蕉', '橘子'] // 检查成员是否存在 const hasApple = await client.sIsMember('fruits', '苹果'); console.log('有苹果:', hasApple); // 1 // 获取集合大小 const fruitsCount = await client.sCard('fruits'); console.log('水果种类数:', fruitsCount); // 3 // 集合运算 const intersection = await client.sInter(['fruits', 'vegetables']); console.log('交集:', intersection); // ['苹果'] const union = await client.sUnion(['fruits', 'vegetables']); console.log('并集:', union); // ['苹果', '香蕉', '橘子', '白菜', '萝卜'] const difference = await client.sDiff(['fruits', 'vegetables']); console.log('差集:', difference); // ['香蕉', '橘子'] // 随机获取成员 const randomFruit = await client.sRandMember('fruits'); console.log('随机水果:', randomFruit); // 移除成员 await client.sRem('fruits', '苹果'); 

5. 有序集合 (Sorted Set) - 排行榜

用途

:排行榜、范围查询、优先级队列

// 添加有分数的成员 await client.zAdd('scores', [ { score: 85, value: '张三' }, { score: 92, value: '李四' }, { score: 78, value: '王五' }, { score: 95, value: '赵六' } ]); // 按分数范围获取成员(升序) const topScores = await client.zRangeWithScores('scores', 0, -1); console.log('成绩排名(升序):', topScores); // [ // { score: 78, value: '王五' }, // { score: 85, value: '张三' }, // { score: 92, value: '李四' }, // { score: 95, value: '赵六' } // ] // 按分数范围获取成员(降序) const topScoresDesc = await client.zRevRangeWithScores('scores', 0, 2); console.log('前三名(降序):', topScoresDesc); // [ // { score: 95, value: '赵六' }, // { score: 92, value: '李四' }, // { score: 85, value: '张三' } // ] // 获取成员排名 const zhangRank = await client.zRevRank('scores', '张三'); console.log('张三排名(从0开始):', zhangRank); // 2 // 获取成员分数 const liScore = await client.zScore('scores', '李四'); console.log('李四分数:', liScore); // 92 // 按分数范围查询 const highScores = await client.zRangeByScore('scores', 90, 100); console.log('90分以上:', highScores); // ['李四', '赵六'] // 增加成员分数 await client.zIncrBy('scores', 5, '张三'); const newScore = await client.zScore('scores', '张三'); console.log('张三新分数:', newScore); // 90 

🎯 实际应用场景详解

1. 缓存管理 (CacheManager)

class CacheManager { constructor(redisClient) { this.redis = redisClient; } // 设置缓存 async set(key, data, expireSeconds = 3600) { const value = JSON.stringify(data); await this.redis.setEx(key, expireSeconds, value); console.log(`缓存已设置: ${key}, 过期时间: ${expireSeconds}秒`); } // 获取缓存 async get(key) { const value = await this.redis.get(key); if (value) { console.log(`缓存命中: ${key}`); return JSON.parse(value); } console.log(`缓存未命中: ${key}`); return null; } // 删除缓存 async del(key) { const result = await this.redis.del(key); console.log(`缓存已删除: ${key}`); return result; } // 缓存用户信息示例 async cacheUser(userId, userData) { const key = `user:${userId}`; await this.set(key, userData, 1800); // 30分钟过期 } async getUser(userId) { const key = `user:${userId}`; return await this.get(key); } } // 使用示例 const cache = new CacheManager(client); await cache.cacheUser(123, { name: '张三', email: 'zhang@example.com' }); const cachedUser = await cache.getUser(123); console.log('缓存的用户:', cachedUser); 

2. 会话管理 (SessionManager)

class SessionManager { constructor(redisClient) { this.redis = redisClient; this.sessionPrefix = 'session:'; this.sessionExpire = 7200; // 2小时 } // 创建会话 async createSession(sessionId, userData) { const key = this.sessionPrefix + sessionId; const sessionData = { ...userData, createdAt: new Date().toISOString(), lastAccess: new Date().toISOString() }; await this.redis.setEx(key, this.sessionExpire, JSON.stringify(sessionData)); console.log(`会话已创建: ${sessionId}`); return sessionId; } // 获取会话 async getSession(sessionId) { const key = this.sessionPrefix + sessionId; const sessionData = await this.redis.get(key); if (sessionData) { const data = JSON.parse(sessionData); // 更新最后访问时间 data.lastAccess = new Date().toISOString(); await this.redis.setEx(key, this.sessionExpire, JSON.stringify(data)); console.log(`会话已获取: ${sessionId}`); return data; } console.log(`会话不存在: ${sessionId}`); return null; } // 删除会话 async destroySession(sessionId) { const key = this.sessionPrefix + sessionId; const result = await this.redis.del(key); console.log(`会话已删除: ${sessionId}`); return result; } } // 使用示例 const session = new SessionManager(client); const sessionId = 'sess_' + Date.now(); await session.createSession(sessionId, { userId: 123, username: '张三' }); const sessionData = await session.getSession(sessionId); console.log('会话数据:', sessionData); 

3. 限流器 (RateLimiter)

class RateLimiter { constructor(redisClient) { this.redis = redisClient; } // 固定窗口限流 async checkLimit(key, limit, windowSeconds) { const current = await this.redis.incr(key); if (current === 1) { // 第一次访问,设置过期时间 await this.redis.expire(key, windowSeconds); } const result = { allowed: current <= limit, current: current, limit: limit, remaining: Math.max(0, limit - current) }; console.log(`限流检查: ${key}`, result); return result; } // API限流示例 async checkApiLimit(userId, endpoint) { const key = `rate_limit:${userId}:${endpoint}`; return await this.checkLimit(key, 100, 3600); // 每小时100次 } // IP限流示例 async checkIpLimit(ip, limit = 1000, windowSeconds = 3600) { const key = `rate_limit:ip:${ip}`; return await this.checkLimit(key, limit, windowSeconds); } } // 使用示例 const rateLimiter = new RateLimiter(client); const limitCheck = await rateLimiter.checkApiLimit(123, '/api/data'); if (!limitCheck.allowed) { console.log('请求被限流'); } else { console.log(`剩余请求次数: ${limitCheck.remaining}`); } 

4. 消息队列 (MessageQueue)

class MessageQueue { constructor(redisClient) { this.redis = redisClient; } // 发送消息到队列 async sendMessage(queueName, message) { const messageData = { id: Date.now().toString(), data: message, timestamp: new Date().toISOString() }; await this.redis.lPush(queueName, JSON.stringify(messageData)); console.log(`消息已发送到队列 ${queueName}:`, messageData.id); } // 从队列获取消息(阻塞模式) async receiveMessage(queueName, timeoutSeconds = 0) { const result = await this.redis.brPop(queueName, timeoutSeconds); if (result) { const message = JSON.parse(result.element); console.log(`从队列 ${queueName} 收到消息:`, message.id); return message; } console.log(`队列 ${queueName} 超时,未收到消息`); return null; } // 获取队列长度 async getQueueLength(queueName) { const length = await this.redis.lLen(queueName); console.log(`队列 ${queueName} 长度: ${length}`); return length; } // 处理队列消息 async processQueue(queueName, processor, batchSize = 1) { console.log(`开始处理队列: ${queueName}`); while (true) { try { const messages = []; for (let i = 0; i < batchSize; i++) { const message = await this.receiveMessage(queueName, 5); // 5秒超时 if (message) { messages.push(message); } else { break; } } if (messages.length > 0) { await processor(messages); } else { console.log('队列为空,等待新消息...'); } } catch (error) { console.error('处理队列消息出错:', error); await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒后重试 } } } } // 使用示例 const queue = new MessageQueue(client); await queue.sendMessage('task_queue', { task: 'process_data', userId: 123 }); console.log('队列长度:', await queue.getQueueLength('task_queue')); // 消息处理器 const messageProcessor = async (messages) => { for (const message of messages) { console.log('处理消息:', message.data); // 模拟处理时间 await new Promise(resolve => setTimeout(resolve, 1000)); } }; // 启动队列处理器 // queue.processQueue('task_queue', messageProcessor); 

5. 分布式锁 (DistributedLock)

class DistributedLock { constructor(redisClient) { this.redis = redisClient; } // 获取锁 async acquireLock(lockKey, lockValue, expireSeconds = 30) { const result = await this.redis.set(lockKey, lockValue, { NX: true, // 只在键不存在时设置 EX: expireSeconds // 设置过期时间 }); const acquired = result === 'OK'; console.log(`锁获取${acquired ? '成功' : '失败'}: ${lockKey}`); return acquired; } // 释放锁 async releaseLock(lockKey, lockValue) { const luaScript = ` if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end `; const result = await this.redis.eval(luaScript, { keys: [lockKey], arguments: [lockValue] }); const released = result === 1; console.log(`锁释放${released ? '成功' : '失败'}: ${lockKey}`); return released; } // 使用锁执行操作 async withLock(lockKey, operation, expireSeconds = 30) { const lockValue = `${Date.now()}-${Math.random()}`; const acquired = await this.acquireLock(lockKey, lockValue, expireSeconds); if (!acquired) { throw new Error('无法获取锁'); } try { console.log(`开始执行需要锁保护的操作: ${lockKey}`); const result = await operation(); console.log(`操作执行完成: ${lockKey}`); return result; } finally { await this.releaseLock(lockKey, lockValue); } } // 尝试获取锁(非阻塞) async tryLock(lockKey, operation, expireSeconds = 30) { const lockValue = `${Date.now()}-${Math.random()}`; const acquired = await this.acquireLock(lockKey, lockValue, expireSeconds); if (!acquired) { console.log(`锁被占用,跳过操作: ${lockKey}`); return null; } try { return await operation(); } finally { await this.releaseLock(lockKey, lockValue); } } } // 使用示例 const lock = new DistributedLock(client); // 方式1:使用 withLock(阻塞) await lock.withLock('resource_lock', async () => { console.log('执行需要锁保护的操作'); // 模拟耗时操作 await new Promise(resolve => setTimeout(resolve, 1000)); }); // 方式2:使用 tryLock(非阻塞) await lock.tryLock('resource_lock', async () => { console.log('尝试执行操作'); await new Promise(resolve => setTimeout(resolve, 1000)); }); 

6. 实时计数器 (RealTimeCounter)

class RealTimeCounter { constructor(redisClient) { this.redis = redisClient; } // 增加计数 async increment(key, amount = 1) { const count = await this.redis.incrBy(key, amount); console.log(`计数器 ${key} 增加 ${amount},当前值: ${count}`); return count; } // 获取计数 async getCount(key) { const count = await this.redis.get(key); const result = count ? parseInt(count) : 0; console.log(`计数器 ${key} 当前值: ${result}`); return result; } // 重置计数 async resetCount(key) { await this.redis.del(key); console.log(`计数器 ${key} 已重置`); } // 网站访问统计示例 async recordPageView(page) { const today = new Date().toISOString().split('T')[0]; const dailyKey = `page_views:${page}:${today}`; const totalKey = `page_views:${page}:total`; await Promise.all([ this.increment(dailyKey), this.increment(totalKey) ]); // 设置日统计过期时间(30天) await this.redis.expire(dailyKey, 30 * 24 * 3600); console.log(`页面 ${page} 访问已记录`); } // 获取页面访问统计 async getPageViewStats(page) { const today = new Date().toISOString().split('T')[0]; const dailyKey = `page_views:${page}:${today}`; const totalKey = `page_views:${page}:total`; const [dailyViews, totalViews] = await Promise.all([ this.getCount(dailyKey), this.getCount(totalKey) ]); return { page, dailyViews, totalViews, date: today }; } // 获取热门页面 async getTopPages(limit = 10) { const pattern = 'page_views:*:total'; const keys = await this.redis.keys(pattern); const pages = []; for (const key of keys) { const page = key.split(':')[1]; const views = await this.getCount(key); pages.push({ page, views }); } return pages .sort((a, b) => b.views - a.views) .slice(0, limit); } } // 使用示例 const counter = new RealTimeCounter(client); await counter.recordPageView('/home'); await counter.recordPageView('/about'); await counter.recordPageView('/home'); const homeStats = await counter.getPageViewStats('/home'); console.log('首页统计:', homeStats); const topPages = await counter.getTopPages(5); console.log('热门页面:', topPages); 

Express + Redis 完整实战

中间件开发

1. 缓存中间件

// 缓存中间件 function cacheMiddleware(expireSeconds = 300) { return async (req, res, next) => { const key = `cache:${req.originalUrl}`; try { const cachedData = await redis.get(key); if (cachedData) { console.log('缓存命中:', key); return res.json(JSON.parse(cachedData)); } // 缓存未命中,继续处理请求 res.originalJson = res.json; res.json = (data) => { // 缓存响应数据 redis.setEx(key, expireSeconds, JSON.stringify(data)); res.originalJson(data); }; next(); } catch (error) { console.error('缓存错误:', error); next(); } }; } 

2. 限流中间件

// 限流中间件 function rateLimitMiddleware(maxRequests = 100, windowSeconds = 3600) { return async (req, res, next) => { const clientIp = req.ip || req.connection.remoteAddress; const key = `rate_limit:${clientIp}`; try { const current = await redis.incr(key); if (current === 1) { await redis.expire(key, windowSeconds); } if (current > maxRequests) { return res.status(429).json({ error: '请求过于频繁', message: `每${windowSeconds}秒最多${maxRequests}次请求`, retryAfter: windowSeconds }); } // 设置响应头 res.set('X-RateLimit-Limit', maxRequests); res.set('X-RateLimit-Remaining', Math.max(0, maxRequests - current)); res.set('X-RateLimit-Reset', new Date(Date.now() + windowSeconds * 1000).toISOString()); next(); } catch (error) { console.error('限流错误:', error); next(); } }; } 

3. 会话中间件

// 会话中间件 async function sessionMiddleware(req, res, next) { const sessionId = req.headers['x-session-id']; if (sessionId) { try { const sessionData = await redis.get(`session:${sessionId}`); if (sessionData) { req.session = JSON.parse(sessionData); // 延长会话有效期 await redis.expire(`session:${sessionId}`, 3600); console.log('会话已加载:', sessionId); } } catch (error) { console.error('会话错误:', error); } } next(); } 

API 路由实现

1. 用户认证

// 用户登录 app.post('/api/login', async (req, res) => { const { username, password } = req.body; // 简化的用户验证 if (username === 'admin' && password === 'password') { const sessionId = 'sess_' + Date.now() + '_' + Math.random(); const sessionData = { userId: 1, username: 'admin', loginTime: new Date().toISOString() }; await redis.setEx(`session:${sessionId}`, 3600, JSON.stringify(sessionData)); res.json({ success: true, sessionId: sessionId, message: '登录成功' }); } else { res.status(401).json({ success: false, message: '用户名或密码错误' }); } }); // 用户信息(需要会话) app.get('/api/user', async (req, res) => { if (!req.session) { return res.status(401).json({ success: false, message: '未登录' }); } res.json({ success: true, user: req.session }); }); 

2. 数据缓存

// 获取用户列表(带缓存) app.get('/api/users', cacheMiddleware(600), async (req, res) => { // 模拟数据库查询 console.log('从数据库获取用户列表...'); await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟延迟 const users = [ { id: 1, name: '张三', email: 'zhang@example.com' }, { id: 2, name: '李四', email: 'li@example.com' }, { id: 3, name: '王五', email: 'wang@example.com' } ]; res.json({ success: true, data: users }); }); 

3. 访问统计

// 获取访问统计 app.get('/api/stats/page-views', async (req, res) => { const { page = '/home' } = req.query; const today = new Date().toISOString().split('T')[0]; const [dailyViews, totalViews] = await Promise.all([ redis.get(`page_views:${page}:${today}`), redis.get(`page_views:${page}:total`) ]); res.json({ success: true, data: { page: page, dailyViews: parseInt(dailyViews) || 0, totalViews: parseInt(totalViews) || 0, date: today } }); }); // 记录页面访问 app.post('/api/stats/page-view', async (req, res) => { const { page } = req.body; if (!page) { return res.status(400).json({ success: false, message: '页面参数必填' }); } const today = new Date().toISOString().split('T')[0]; const dailyKey = `page_views:${page}:${today}`; const totalKey = `page_views:${page}:total`; await Promise.all([ redis.incr(dailyKey), redis.incr(totalKey) ]); // 设置日统计过期时间 await redis.expire(dailyKey, 30 * 24 * 3600); res.json({ success: true, message: '访问记录已更新' }); }); 

4. 消息队列

// 发送消息到队列 app.post('/api/queue/send', rateLimitMiddleware(10, 60), async (req, res) => { const { message, priority = 0 } = req.body; if (!message) { return res.status(400).json({ success: false, message: '消息内容不能为空' }); } const messageData = { id: Date.now().toString(), message: message, priority: priority, timestamp: new Date().toISOString() }; // 根据优先级选择队列 const queueName = priority > 0 ? 'high_priority_queue' : 'normal_queue'; await redis.lPush(queueName, JSON.stringify(messageData)); res.json({ success: true, message: '消息已添加到队列', queueName: queueName, messageId: messageData.id }); }); // 获取队列状态 app.get('/api/queue/status', async (req, res) => { const [normalQueueLength, highPriorityQueueLength] = await Promise.all([ redis.lLen('normal_queue'), redis.lLen('high_priority_queue') ]); res.json({ success: true, data: { normalQueue: normalQueueLength, highPriorityQueue: highPriorityQueueLength, total: normalQueueLength + highPriorityQueueLength } }); }); 

5. 缓存管理

// 清空缓存 app.delete('/api/cache', async (req, res) => { const { pattern = 'cache:*' } = req.query; try { const keys = await redis.keys(pattern); if (keys.length > 0) { await redis.del(keys); } res.json({ success: true, message: `清除了 ${keys.length} 个缓存项`, clearedKeys: keys }); } catch (error) { res.status(500).json({ success: false, message: '清除缓存失败', error: error.message }); } }); // 获取缓存信息 app.get('/api/cache/info', async (req, res) => { try { const keys = await redis.keys('cache:*'); const cacheInfo = []; for (const key of keys) { const ttl = await redis.ttl(key); cacheInfo.push({ key, ttl: ttl > 0 ? ttl : '永不过期' }); } res.json({ success: true, data: { totalKeys: keys.length, caches: cacheInfo } }); } catch (error) { res.status(500).json({ success: false, message: '获取缓存信息失败', error: error.message }); } }); 

6. 健康检查

// 健康检查 app.get('/api/health', async (req, res) => { try { const startTime = Date.now(); await redis.ping(); const responseTime = Date.now() - startTime; res.json({ success: true, message: 'Service is healthy', redis: 'connected', responseTime: `${responseTime}ms`, timestamp: new Date().toISOString() }); } catch (error) { res.status(500).json({ success: false, message: 'Service is unhealthy', redis: 'disconnected', error: error.message, timestamp: new Date().toISOString() }); } }); 

完整应用启动

// expressRedis.js 完整示例 import express from 'express'; import { createClient } from 'redis'; const app = express(); const port = 3000; // Redis 客户端 const redis = createClient(); await redis.connect(); app.use(express.json()); // 应用中间件 app.use(sessionMiddleware); // 路由定义 // ... (所有上述路由) // 错误处理 app.use((error, req, res, next) => { console.error('应用错误:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); }); // 启动服务器 app.listen(port, () => { console.log(`服务器运行在 http://localhost:${port}`); }); 

最佳实践与优化

1. 连接管理

// 连接池配置 const client = createClient({ socket: { host: 'localhost', port: 6379, connectTimeout: 10000, lazyConnect: true }, retryDelayOnFailover: 100, maxRetriesPerRequest: 3 }); // 优雅关闭 process.on('SIGINT', async () => { console.log('正在关闭 Redis 连接...'); await client.disconnect(); process.exit(0); }); 

2. 键命名规范

# 用户相关 user:123 # 用户基本信息 user:123:profile # 用户详细资料 user:123:sessions # 用户会话列表 # 缓存相关 cache:api:users # API 缓存 cache:page:home # 页面缓存 # 会话相关 session:abc123 # 会话数据 session:abc123:permissions # 会话权限 # 限流相关 rate_limit:ip:192.168.1.1 # IP 限流 rate_limit:user:123:api # 用户 API 限流 # 统计相关 stats:page_views:home:2024-01-01 # 日统计 stats:page_views:home:total # 总统计 

3. 过期时间设置

// 缓存过期时间 const CACHE_EXPIRES = { USER_INFO: 1800, // 30分钟 API_RESPONSE: 300, // 5分钟 SESSION: 7200, // 2小时 TEMP_DATA: 86400, // 1天 STATS_DAILY: 2592000 // 30天 }; // 设置过期时间 await redis.setEx(key, CACHE_EXPIRES.USER_INFO, value); 

4. 错误处理与降级

class RedisService { constructor(redisClient) { this.redis = redisClient; } async safeGet(key, fallback = null) { try { const value = await this.redis.get(key); return value ? JSON.parse(value) : fallback; } catch (error) { console.error('Redis GET 错误:', error); return fallback; } } async safeSet(key, value, expireSeconds = 3600) { try { await this.redis.setEx(key, expireSeconds, JSON.stringify(value)); return true; } catch (error) { console.error('Redis SET 错误:', error); return false; } } } 

5. 性能优化

// 使用管道批量操作 const pipeline = redis.multi(); pipeline.set('key1', 'value1'); pipeline.set('key2', 'value2'); pipeline.set('key3', 'value3'); const results = await pipeline.exec(); // 使用事务 const multi = redis.multi(); multi.incr('counter'); multi.expire('counter', 3600); const results = await multi.exec(); 

❓ 常见问题与解决方案

Q1: 如何选择合适的数据类型?

  • 字符串

    :简单键值对、计数器、缓存
  • 哈希

    :对象属性存储、用户信息
  • 列表

    :队列、时间线、栈
  • 集合

    :标签、去重、好友关系
  • 有序集合

    :排行榜、范围查询、优先级队列

Q2: 如何处理 Redis 连接失败?

// 重连机制 client.on('error', async (err) => { console.error('Redis 连接错误:', err); // 实现重连逻辑 setTimeout(async () => { try { await client.connect(); console.log('Redis 重连成功'); } catch (error) { console.error('Redis 重连失败:', error); } }, 5000); }); 

Q3: 如何优化 Redis 性能?

  • 使用管道(Pipeline)批量操作
  • 合理设置过期时间
  • 避免大键操作
  • 使用合适的数据类型
  • 监控内存使用情况

Q4: 如何处理内存不足?

// 监控内存使用 const info = await redis.memory('usage'); console.log('Redis 内存使用:', info); // 设置最大内存 await redis.config('set', 'maxmemory', '100mb'); await redis.config('set', 'maxmemory-policy', 'allkeys-lru'); 

Q5: 如何实现分布式锁?

// 使用 Lua 脚本确保原子性 const lockScript = ` if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end `; const result = await redis.eval(lockScript, { keys: [lockKey], arguments: [lockValue] }); 

🔗 相关链接

  • Redis 官方文档
  • Node.js Redis 客户端
  • 本文作者:WAP站长网
  • 本文链接: https://wapzz.net/post-27874.html
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
本站部分内容来源于网络转载,仅供学习交流使用。如涉及版权问题,请及时联系我们,我们将第一时间处理。
文章很赞!支持一下吧 还没有人为TA充电
为TA充电
还没有人为TA充电
0
0
  • 支付宝打赏
    支付宝扫一扫
  • 微信打赏
    微信扫一扫
感谢支持
文章很赞!支持一下吧
关于作者
2.8W+
9
1
2
WAP站长官方

MySQL 字符串替换实战指南:2 个函数搞定 90% 业务需求

上一篇

使用Yolo12算法进行区域内实时目标计数

下一篇
评论区
内容为空

这一切,似未曾拥有

  • 复制图片
按住ctrl可打开默认菜单