Redis入门与实践
Redis 基础概念
Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,可以用作数据库、缓存、消息代理和流引擎。Redis 提供了多种数据结构,如字符串、哈希、列表、集合、有序集合等。
Redis 核心特性
// Redis 核心特性
const redisFeatures = {
内存存储: "数据存储在内存中,读写速度极快",
持久化: "支持RDB快照和AOF日志两种持久化方式",
数据结构: "丰富的数据类型支持",
高可用: "支持主从复制、哨兵模式、集群模式",
事务支持: "支持事务和管道操作",
发布订阅: "内置消息发布订阅功能",
脚本支持: "支持Lua脚本执行"
};
Redis 应用场景
graph TD
A[Redis应用场景] --> B[缓存系统]
A --> C[会话存储]
A --> D[实时应用]
A --> E[消息队列]
A --> F[分布式锁]
B --> G[页面缓存]
B --> H[数据库查询缓存]
B --> I[API响应缓存]
C --> J[用户登录状态]
C --> K[购物车数据]
C --> L[临时数据存储]
D --> M[排行榜]
D --> N[计数器]
D --> O[限流控制]
E --> P[任务队列]
E --> Q[发布订阅]
E --> R[流处理]
F --> S[并发控制]
F --> T[资源访问控制]
Redis vs 传统数据库
// Redis 与传统数据库对比
const redisVsDatabase = {
Redis: {
存储方式: "内存存储,可选持久化",
数据模型: "键值对,多种数据结构",
查询语言: "命令式操作",
ACID: "有限支持(事务、原子性)",
扩展性: "主从复制、集群分片",
性能: "极高(微秒级)",
使用场景: "缓存、会话、实时计算"
},
传统数据库: {
存储方式: "磁盘存储",
数据模型: "关系模型、表结构",
查询语言: "SQL",
ACID: "完整支持",
扩展性: "垂直扩展为主",
性能: "较高(毫秒级)",
使用场景: "事务处理、数据分析"
}
};
Docker 安装 Redis
基础安装
# 拉取 Redis 官方镜像
docker pull redis:7-alpine
# 运行 Redis 容器
docker run -d \
--name redis-server \
-p 6379:6379 \
redis:7-alpine
# 进入 Redis 命令行
docker exec -it redis-server redis-cli
生产环境配置
// Redis 生产环境配置参数
const redisProductionConfig = {
内存管理: {
maxmemory: "2gb",
maxmemoryPolicy: "allkeys-lru"
},
持久化: {
save: "900 1 300 10 60 10000",
appendonly: "yes",
appendfsync: "everysec"
},
安全配置: {
requirepass: "strong_password",
bindAddress: "127.0.0.1"
},
网络配置: {
port: 6379,
tcpBacklog: 511,
timeout: 300
}
};
# 创建 Redis 配置文件
cat > redis.conf << EOF
# 网络配置
bind 127.0.0.1
port 6379
timeout 300
# 内存配置
maxmemory 2gb
maxmemory-policy allkeys-lru
# 持久化配置
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec
# 安全配置
requirepass your_strong_password_here
EOF
# 使用自定义配置运行 Redis
docker run -d \
--name redis-production \
-p 6379:6379 \
-v $(pwd)/redis.conf:/usr/local/etc/redis/redis.conf \
-v redis-data:/data \
redis:7-alpine \
redis-server /usr/local/etc/redis/redis.conf
Redis 启动流程
flowchart TD
A[启动 Redis 服务] --> B[加载配置文件]
B --> C[初始化数据结构]
C --> D[加载持久化数据]
D --> E{AOF 文件存在?}
E --> F[是] --> G[加载 AOF 文件]
E --> H[否] --> I{RDB 文件存在?}
I --> J[是] --> K[加载 RDB 文件]
I --> L[否] --> M[空数据库启动]
G --> N[启动事件循环]
K --> N
M --> N
N --> O[监听客户端连接]
O --> P[Redis 服务就绪]
Redis 核心操作与 SQL 对比
设置键值对
Redis 操作
// Redis SET 操作
const redisSetOperations = {
基本设置: "SET key value",
带过期时间: "SET key value EX seconds",
条件设置: "SET key value NX", // 仅当key不存在时设置
批量设置: "MSET key1 value1 key2 value2"
};
# 基本设置
SET user:1001:name "张三"
SET user:1001:email "zhangsan@example.com"
# 设置带过期时间的键(30分钟)
SET session:abc123 "user_data" EX 1800
# 条件设置(仅当不存在时)
SET config:app_name "MyApp" NX
# 批量设置
MSET user:1001:age 25 user:1001:city "北京"
SQL 对比
-- SQL 中的等价操作
INSERT INTO users (id, name, email) VALUES (1001, '张三', 'zhangsan@example.com')
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
email = EXCLUDED.email;
-- 会话存储需要专门的表
INSERT INTO sessions (session_id, user_data, expires_at)
VALUES ('abc123', 'user_data', NOW() + INTERVAL '30 minutes');
获取键值
Redis 操作
// Redis GET 操作
const redisGetOperations = {
获取单个值: "GET key",
批量获取: "MGET key1 key2 key3",
获取并设置: "GETSET key newvalue",
模糊匹配: "KEYS pattern",
检查存在: "EXISTS key"
};
# 获取单个值
GET user:1001:name
# 批量获取
MGET user:1001:name user:1001:email user:1001:age
# 获取所有用户相关的键
KEYS user:1001:*
# 检查键是否存在
EXISTS session:abc123
# 获取键的剩余过期时间
TTL session:abc123
SQL 对比
-- SQL 查询操作
SELECT name FROM users WHERE id = 1001;
-- 批量查询
SELECT id, name, email, age FROM users WHERE id IN (1001, 1002, 1003);
-- 模糊匹配
SELECT * FROM sessions WHERE session_id LIKE 'abc%';
-- 检查存在性
SELECT EXISTS(SELECT 1 FROM users WHERE id = 1001);
删除键值
Redis 操作
// Redis DELETE 操作
const redisDeleteOperations = {
删除单个: "DEL key",
删除多个: "DEL key1 key2 key3",
模糊删除: "先KEYS匹配,再DEL删除",
设置过期: "EXPIRE key seconds",
清空数据库: "FLUSHDB"
};
# 删除单个键
DEL user:1001:temp_data
# 删除多个键
DEL user:1001:cache user:1001:session
# 模糊删除(先匹配再删除)
redis-cli KEYS "temp:*" | xargs redis-cli DEL
# 设置键过期时间
EXPIRE user:1001:cache 3600
# 移除键的过期时间
PERSIST user:1001:name
SQL 对比
-- SQL 删除操作
DELETE FROM users WHERE id = 1001;
-- 条件删除
DELETE FROM sessions WHERE expires_at < NOW();
-- 清空表
TRUNCATE TABLE temp_data;
使用 Redis 的多种数据类型
字符串(String)
String 是 Redis 最基础的数据类型,可以存储文本、数字、二进制数据。
// String 类型操作示例
const stringOperations = {
设置: "SET key value",
获取: "GET key",
追加: "APPEND key value",
数值操作: "INCR key / DECR key",
批量操作: "MSET / MGET"
};
# 基础字符串操作
SET user:profile:bio "全栈开发工程师,专注于前端架构设计"
GET user:profile:bio
# 数值操作
SET page:views 1000
INCR page:views # 增加1,返回1001
INCRBY page:views 10 # 增加10,返回1011
DECR page:views # 减少1,返回1010
# 字符串追加
SET log:error "Database connection failed"
APPEND log:error " at 2024-01-15 10:30:00"
# 获取字符串长度
STRLEN log:error
# 获取子字符串
GETRANGE user:profile:bio 0 5
哈希(Hash)
Hash 用于存储对象的多个字段,类似于关系数据库中的行。
// Hash 类型操作示例
const hashOperations = {
设置字段: "HSET key field value",
获取字段: "HGET key field",
批量设置: "HMSET key field1 value1 field2 value2",
获取所有: "HGETALL key",
字段操作: "HDEL / HEXISTS / HLEN"
};
# 设置用户信息
HSET user:1001 name "张三" email "zhangsan@example.com" age 28 city "北京"
# 获取特定字段
HGET user:1001 name
HGET user:1001 email
# 批量获取
HMGET user:1001 name email age
# 获取所有字段
HGETALL user:1001
# 检查字段是否存在
HEXISTS user:1001 phone
# 删除字段
HDEL user:1001 age
# 数值字段操作
HINCRBY user:1001 login_count 1
# 获取所有字段名
HKEYS user:1001
# 获取所有值
HVALS user:1001
列表 (List)
List 是有序的字符串列表,支持在两端进行推入和弹出操作。
// List 类型操作示例
const listOperations = {
左侧推入: "LPUSH key value",
右侧推入: "RPUSH key value",
左侧弹出: "LPOP key",
右侧弹出: "RPOP key",
范围获取: "LRANGE key start stop",
长度获取: "LLEN key"
};
# 创建任务队列
RPUSH task:queue "发送邮件" "生成报告" "清理缓存"
# 获取队列长度
LLEN task:queue
# 查看队列内容
LRANGE task:queue 0 -1
# 从队列头部取出任务
LPOP task:queue
# 在指定位置插入
LINSERT task:queue BEFORE "生成报告" "数据备份"
# 设置指定索引的值
LSET task:queue 1 "更新报告模板"
# 移除指定值
LREM task:queue 1 "清理缓存"
# 保留指定范围的元素
LTRIM task:queue 0 9 # 只保留前10个元素
# 阻塞式弹出(用于实现队列)
BLPOP task:queue 30 # 30秒超时
集合 (Set)
Set 是无序的字符串集合,所有元素都是唯一的。
// Set 类型操作示例
const setOperations = {
添加元素: "SADD key member",
移除元素: "SREM key member",
检查成员: "SISMEMBER key member",
获取所有: "SMEMBERS key",
集合运算: "SINTER / SUNION / SDIFF",
随机获取: "SRANDMEMBER key count"
};
# 创建用户标签集合
SADD user:1001:tags "JavaScript" "React" "Node.js" "PostgreSQL"
SADD user:1002:tags "Python" "Django" "PostgreSQL" "Redis"
# 检查用户是否有特定标签
SISMEMBER user:1001:tags "React"
# 获取用户所有标签
SMEMBERS user:1001:tags
# 获取集合大小
SCARD user:1001:tags
# 两个用户的共同技能(交集)
SINTER user:1001:tags user:1002:tags
# 所有技能的并集
SUNION user:1001:tags user:1002:tags
# 用户1独有的技能(差集)
SDIFF user:1001:tags user:1002:tags
# 随机获取技能
SRANDMEMBER user:1001:tags 2
# 移除标签
SREM user:1001:tags "Node.js"
# 将元素从一个集合移动到另一个
SMOVE user:1001:tags user:1001:archived_tags "React"
有序集合 (Sorted Set)
Sorted Set 是有序的集合,每个元素都有一个分数(score),按分数排序。
// Sorted Set 类型操作示例
const sortedSetOperations = {
添加元素: "ZADD key score member",
范围查询: "ZRANGE key start stop",
分数查询: "ZRANGEBYSCORE key min max",
排名查询: "ZRANK key member",
分数获取: "ZSCORE key member",
移除元素: "ZREM key member"
};
# 创建用户积分排行榜
ZADD leaderboard 1500 "张三" 1200 "李四" 1800 "王五" 1350 "赵六"
# 获取排行榜前3名(按分数从高到低)
ZREVRANGE leaderboard 0 2 WITHSCORES
# 获取排行榜后3名(按分数从低到高)
ZRANGE leaderboard 0 2 WITHSCORES
# 获取指定分数范围的用户
ZRANGEBYSCORE leaderboard 1300 1600 WITHSCORES
# 获取用户排名(从0开始,分数从低到高)
ZRANK leaderboard "张三"
# 获取用户排名(从0开始,分数从高到低)
ZREVRANK leaderboard "张三"
# 获取用户分数
ZSCORE leaderboard "张三"
# 增加用户分数
ZINCRBY leaderboard 100 "张三"
# 获取指定排名范围的用户数量
ZCOUNT leaderboard 1200 1500
# 移除分数最低的用户
ZREMRANGEBYRANK leaderboard 0 0
# 移除分数在指定范围内的用户
ZREMRANGEBYSCORE leaderboard 0 1000
Redis 数据类型选择流程
flowchart TD
A[选择Redis数据类型] --> B{数据特征}
B --> C[简单键值对]
B --> D[对象多属性]
B --> E[有序列表]
B --> F[唯一元素集合]
B --> G[带权重排序]
C --> H[String]
D --> I[Hash]
E --> J[List]
F --> K[Set]
G --> L[Sorted Set]
H --> M[缓存、计数器、会话]
I --> N[用户信息、配置对象]
J --> O[消息队列、时间线]
K --> P[标签、去重、关系]
L --> Q[排行榜、评分系统]
subgraph "使用建议"
R[考虑内存使用]
S[考虑操作复杂度]
T[考虑业务场景]
end
用户登录身份存储与 PostgreSQL 集成
使用 Redis 存储用户登录会话
会话管理架构
sequenceDiagram
participant Client as 客户端
participant App as 应用服务
participant Redis as Redis缓存
participant DB as PostgreSQL
Client->>App: 用户登录请求
App->>DB: 验证用户凭据
DB-->>App: 返回用户信息
App->>Redis: 存储会话数据
Redis-->>App: 返回会话ID
App-->>Client: 返回登录成功+Token
Client->>App: 访问受保护资源
App->>Redis: 验证会话
Redis-->>App: 返回会话数据
App-->>Client: 返回受保护资源
实现会话存储
// 会话管理配置
const sessionConfig = {
prefix: "session:",
expire: 3600 * 24 * 7, // 7天过期
refreshThreshold: 3600, // 1小时内活动则刷新
userPrefix: "user:session:",
maxSessions: 5 // 每用户最大会话数
};
# 用户登录成功后存储会话
SET session:abc123def456 "{\"userId\": 1001, \"username\": \"zhangsan\", \"loginTime\": \"2024-01-15T10:30:00Z\", \"ip\": \"192.168.1.100\"}" EX 604800
# 为用户维护活跃会话列表
SADD user:session:1001 "abc123def456"
# 设置用户会话列表过期时间
EXPIRE user:session:1001 604800
# 存储会话元数据
HSET session:meta:abc123def456 user_id 1001 created_at "2024-01-15T10:30:00Z" last_active "2024-01-15T10:30:00Z" ip "192.168.1.100"
EXPIRE session:meta:abc123def456 604800
会话验证与刷新
// 会话验证逻辑
const sessionValidation = {
验证流程: [
"检查会话是否存在",
"检查会话是否过期",
"更新最后活跃时间",
"检查IP变化(可选)",
"返回用户信息"
],
刷新条件: [
"距离上次活跃超过阈值",
"用户执行重要操作",
"主动刷新请求"
]
};
# 验证会话并获取用户信息
GET session:abc123def456
# 检查会话是否存在
EXISTS session:abc123def456
# 更新会话最后活跃时间
HSET session:meta:abc123def456 last_active "2024-01-15T11:30:00Z"
EXPIRE session:abc123def456 604800
# 获取用户所有活跃会话
SMEMBERS user:session:1001
# 检查用户会话数量是否超限
SCARD user:session:1001
# 清理过期的用户会话引用
# 这需要应用程序定期执行清理任务
会话清理与安全
# 用户主动登出
DEL session:abc123def456
DEL session:meta:abc123def456
SREM user:session:1001 "abc123def456"
# 清理用户所有会话(强制登出)
# 1. 获取用户所有会话
SMEMBERS user:session:1001
# 2. 删除所有会话数据(需要脚本批量执行)
# 3. 清空用户会话集合
DEL user:session:1001
# 设置会话黑名单(用于安全控制)
SADD session:blacklist "abc123def456"
EXPIRE session:blacklist 604800
将 Redis 与 PostgreSQL 集成
集成架构设计
graph TD
subgraph "应用层"
A[Web应用] --> B[会话中间件]
B --> C[缓存管理器]
end
subgraph "缓存层"
D[Redis集群]
E[会话存储]
F[用户缓存]
G[权限缓存]
D --> E
D --> F
D --> G
end
subgraph "数据层"
H[PostgreSQL主库]
I[用户表]
J[权限表]
K[审计日志表]
H --> I
H --> J
H --> K
end
C --> D
C --> H
subgraph "数据流"
L[热数据] --> D
M[冷数据] --> H
N[持久化数据] --> H
end
用户信息缓存策略
// 缓存策略配置
const cacheStrategy = {
用户基础信息: {
key: "user:info:{userId}",
expire: 3600, // 1小时
source: "users表",
updateTrigger: ["用户信息变更", "定期刷新"]
},
用户权限: {
key: "user:permissions:{userId}",
expire: 1800, // 30分钟
source: "用户权限关联查询",
updateTrigger: ["权限变更", "角色变更"]
},
用户偏好: {
key: "user:preferences:{userId}",
expire: 7200, // 2小时
source: "用户配置表",
updateTrigger: ["设置变更"]
}
};
# 缓存用户基础信息
HSET user:info:1001 \
username "zhangsan" \
email "zhangsan@example.com" \
display_name "张三" \
avatar_url "/avatars/1001.jpg" \
last_login "2024-01-15T10:30:00Z"
EXPIRE user:info:1001 3600
# 缓存用户权限
SADD user:permissions:1001 "user:read" "user:update" "order:create" "order:read"
EXPIRE user:permissions:1001 1800
# 缓存用户角色
SADD user:roles:1001 "customer" "premium_user"
EXPIRE user:roles:1001 1800
# 缓存用户统计信息
HSET user:stats:1001 \
login_count 156 \
last_order_date "2024-01-10" \
total_orders 23 \
loyalty_points 1250
EXPIRE user:stats:1001 7200
缓存更新策略
// 缓存更新模式
const cacheUpdatePatterns = {
Cache_Aside: "应用程序负责缓存的读写",
Write_Through: "数据同时写入缓存和数据库",
Write_Behind: "先写缓存,异步写数据库",
Refresh_Ahead: "在缓存过期前主动刷新"
};
// 缓存失效策略
const cacheInvalidation = {
TTL过期: "设置合理的过期时间",
主动失效: "数据变更时删除缓存",
标签失效: "使用标签批量失效相关缓存",
版本控制: "使用版本号控制缓存有效性"
};
# 缓存失效示例
# 1. 用户信息更新后,删除相关缓存
DEL user:info:1001
DEL user:stats:1001
# 2. 批量失效用户相关缓存
redis-cli KEYS "user:*:1001" | xargs redis-cli DEL
# 3. 使用管道操作提高效率
redis-cli --pipe <<EOF
DEL user:info:1001
DEL user:permissions:1001
DEL user:preferences:1001
EOF
# 4. 设置缓存版本控制
SET cache:version:user:1001 "v2024011501"
EXPIRE cache:version:user:1001 86400
数据一致性保证
flowchart TD
A[数据变更请求] --> B[开始数据库事务]
B --> C[更新PostgreSQL]
C --> D{更新成功?}
D --> E[是] --> F[提交事务]
D --> G[否] --> H[回滚事务]
F --> I[删除Redis缓存]
I --> J{缓存删除成功?}
J --> K[是] --> L[返回成功]
J --> M[否] --> N[记录日志,异步清理]
H --> O[返回错误]
N --> P[后台清理任务]
subgraph "一致性策略"
Q[最终一致性]
R[强一致性要求]
S[容错机制]
end
Redis 进阶应用
Redis 事务(Transactions)
Redis 事务允许一组命令在单一步骤中执行,具有隔离性和原子性。
事务特性
// Redis 事务特性
const transactionFeatures = {
原子性: "所有命令要么全部执行,要么全部不执行",
隔离性: "事务中的命令不会被其他客户端插入的命令打断",
一致性: "事务执行前后,数据处于一致状态",
持久性: "取决于Redis的持久化配置"
};
// 事务命令
const transactionCommands = {
MULTI: "开始事务",
EXEC: "执行事务",
DISCARD: "取消事务",
WATCH: "监视键变化",
UNWATCH: "取消监视"
};
使用示例:
# 基础事务示例:转账操作
MULTI
DECRBY account:user1:balance 100
INCRBY account:user2:balance 100
EXEC
# 带条件的事务:库存扣减
WATCH product:1001:stock
stock_value=$(redis-cli GET product:1001:stock)
if [ $stock_value -gt 0 ]; then
redis-cli MULTI
redis-cli DECR product:1001:stock
redis-cli LPUSH order:queue "product:1001:order:12345"
redis-cli EXEC
else
echo "库存不足"
fi
# 复杂业务事务:用户注册
MULTI
INCR user:count
SET user:1002:profile "{\"name\": \"李四\", \"email\": \"lisi@example.com\"}"
SADD users:active 1002
ZADD users:registered $(date +%s) 1002
EXEC
# 使用 WATCH 实现乐观锁
WATCH user:1001:balance
balance=$(redis-cli GET user:1001:balance)
if [ $balance -ge 50 ]; then
redis-cli MULTI
redis-cli DECRBY user:1001:balance 50
redis-cli INCRBY user:1001:spent 50
redis-cli LPUSH purchase:history "user:1001:purchase:$(date +%s)"
redis-cli EXEC
else
redis-cli UNWATCH
echo "余额不足"
fi
注意事项:
// Redis 事务注意事项
const transactionCaveats = {
非关系型事务: "Redis不支持回滚,命令执行错误会继续执行其他命令",
语法错误: "如果事务中有语法错误,整个事务会被拒绝",
运行时错误: "如果某个命令执行失败,其他命令仍会执行",
WATCH机制: "被监视的键发生变化时,事务会被取消",
性能考虑: "事务会阻塞其他操作,应避免长时间事务"
};
Redis 持久化(Persistence)
Redis 提供两种持久化方式:RDB 快照和 AOF 日志。
RDB 持久化
RDB 是在指定时间间隔内生成数据集的快照。
// RDB 配置参数
const rdbConfig = {
触发条件: {
save_900_1: "900秒内至少1个键发生变化",
save_300_10: "300秒内至少10个键发生变化",
save_60_10000: "60秒内至少10000个键发生变化"
},
优势: [
"文件紧凑,适合备份",
"恢复速度快",
"对性能影响小"
],
劣势: [
"可能丢失最近的数据",
"fork子进程时可能阻塞",
"不适合实时性要求高的场景"
]
};
# RDB 配置示例
save 900 1 # 15分钟内至少1个键变化时保存
save 300 10 # 5分钟内至少10个键变化时保存
save 60 10000 # 1分钟内至少10000个键变化时保存
# 手动触发快照
BGSAVE # 后台保存
SAVE # 前台保存(会阻塞)
# 获取最后一次保存时间
LASTSAVE
# 禁用 RDB
redis-cli CONFIG SET save ""
# 动态修改 RDB 配置
CONFIG SET save "300 10 60 1000"
AOF 持久化
AOF 记录每个写操作命令,通过重新执行命令来恢复数据。
// AOF 配置参数
const aofConfig = {
同步策略: {
always: "每个写命令都同步到磁盘(最安全但最慢)",
everysec: "每秒同步一次(默认,平衡安全性和性能)",
no: "由操作系统决定何时同步(最快但最不安全)"
},
重写机制: {
auto_aof_rewrite_percentage: "AOF文件增长百分比",
auto_aof_rewrite_min_size: "AOF文件最小重写大小"
}
};
# AOF 配置
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 手动触发 AOF 重写
BGREWRITEAOF
# 检查 AOF 状态
INFO persistence
# 修复损坏的 AOF 文件
redis-check-aof --fix appendonly.aof
持久化策略选择
flowchart TD
A[选择持久化策略] --> B{业务需求}
B --> C[数据安全性优先]
B --> D[性能优先]
B --> E[存储空间优先]
B --> F[恢复速度优先]
C --> G[AOF + everysec]
D --> H[RDB + 适当间隔]
E --> I[RDB]
F --> J[RDB]
G --> K[最小数据丢失]
H --> L[高性能写入]
I --> M[文件体积小]
J --> N[快速启动]
subgraph "混合策略"
O[RDB + AOF] --> P[最佳可靠性]
Q[定期RDB + 短期AOF] --> R[平衡方案]
end
Redis 发布/订阅 (Pub/Sub)
Redis 的发布/订阅功能允许客户端订阅频道,并接收发布到这些频道的消息。
发布/订阅模型
graph TD
subgraph "发布者"
A[应用A] --> B[PUBLISH]
C[应用B] --> B
D[应用C] --> B
end
subgraph "Redis服务器"
B --> E[频道路由]
E --> F[user:notifications]
E --> G[system:alerts]
E --> H[order:updates]
end
subgraph "订阅者"
F --> I[客户端1]
F --> J[客户端2]
G --> K[监控系统]
H --> L[订单服务]
H --> M[通知服务]
end
发布/订阅示例
// 发布订阅场景
const pubsubScenarios = {
实时通知: "用户通知、系统消息",
聊天系统: "群聊、私聊消息",
事件驱动: "订单状态变更、库存更新",
监控告警: "系统状态、性能指标",
日志收集: "应用日志、错误日志"
};
# 订阅频道(客户端1)
SUBSCRIBE user:notifications system:alerts
# 模式订阅(订阅所有用户通知)
PSUBSCRIBE user:*
# 发布消息
PUBLISH user:notifications "{\"type\": \"order\", \"message\": \"您的订单已发货\", \"userId\": 1001}"
PUBLISH system:alerts "{\"level\": \"warning\", \"message\": \"CPU使用率超过80%\", \"timestamp\": \"2024-01-15T10:30:00Z\"}"
# 查看频道信息
PUBSUB CHANNELS # 查看所有活跃频道
PUBSUB CHANNELS user:* # 查看匹配模式的频道
PUBSUB NUMSUB user:notifications # 查看频道订阅者数量
# 取消订阅
UNSUBSCRIBE user:notifications
PUNSUBSCRIBE user:*
消息队列实现
# 使用 List 实现消息队列
# 生产者
LPUSH message:queue "{\"id\": 1, \"type\": \"email\", \"recipient\": \"user@example.com\"}"
LPUSH message:queue "{\"id\": 2, \"type\": \"sms\", \"recipient\": \"+8613812345678\"}"
# 消费者(阻塞式)
BRPOP message:queue 30 # 30秒超时
# 使用 Sorted Set 实现延迟队列
# 添加延迟任务(10分钟后执行)
ZADD delay:queue $(expr $(date +%s) + 600) "{\"id\": 1, \"task\": \"send_reminder\"}"
# 获取到期任务
ZRANGEBYSCORE delay:queue 0 $(date +%s) LIMIT 0 10
Redis 流 (Streams)
Redis Streams 是 Redis 5.0 引入的新数据类型,用于构建高性能的消息队列和事件存储系统。
创建和使用 Redis 流
// Streams 核心概念
const streamsConcepts = {
Entry: "流中的单条消息,包含ID和字段值对",
ID: "消息的唯一标识符,格式为 millisecondsTime-sequenceNumber",
ConsumerGroup: "消费者组,用于协调多个消费者",
PendingList: "已分发但未确认的消息列表",
LastDeliveredID: "消费者组最后分发的消息ID"
};
# 创建流并添加消息
XADD user:actions * user_id 1001 action "login" ip "192.168.1.100" timestamp "2024-01-15T10:30:00Z"
XADD user:actions * user_id 1002 action "purchase" product_id 2001 amount 299.99
# 自动生成ID添加消息
XADD order:events * order_id 12345 status "created" customer_id 1001
XADD order:events * order_id 12345 status "paid" payment_method "credit_card"
# 读取流中的消息
XRANGE user:actions - + # 读取所有消息
XRANGE user:actions - + COUNT 10 # 读取前10条消息
XREVRANGE user:actions + - COUNT 5 # 逆序读取最新5条消息
# 从指定时间开始读取
XRANGE user:actions "1705304400000-0" +
# 实时读取新消息
XREAD COUNT 1 BLOCK 1000 STREAMS user:actions $ # 读取最新消息,1秒超时
# 获取流信息
XLEN user:actions # 获取流长度
XINFO STREAM user:actions # 获取流详细信息
消费者组(Consumer Groups)
# 创建消费者组
XGROUP CREATE user:actions user_group $ MKSTREAM
# 创建从头开始消费的消费者组
XGROUP CREATE user:actions analytics_group 0
# 消费者从组中读取消息
XREADGROUP GROUP user_group consumer1 COUNT 1 BLOCK 1000 STREAMS user:actions >
# 处理消息后确认
XACK user:actions user_group "1705304461000-0"
# 查看待处理消息
XPENDING user:actions user_group
# 查看特定消费者的待处理消息
XPENDING user:actions user_group - + 10 consumer1
# 声明消息所有权(处理故障消费者的消息)
XCLAIM user:actions user_group consumer2 3600000 "1705304461000-0"
# 删除消费者组
XGROUP DESTROY user:actions old_group
# 删除消费者
XGROUP DELCONSUMER user:actions user_group inactive_consumer
Streams 消费模式
graph TD
subgraph "生产者"
A[应用服务1] --> B[XADD]
C[应用服务2] --> B
D[应用服务3] --> B
end
subgraph "Redis Stream"
B --> E[user:actions流]
E --> F[消息1: ID1]
E --> G[消息2: ID2]
E --> H[消息3: ID3]
end
subgraph "消费者组1"
I[consumer1] --> J[XREADGROUP]
K[consumer2] --> J
J --> E
end
subgraph "消费者组2"
L[analytics_consumer] --> M[XREADGROUP]
M --> E
end
subgraph "消息确认"
N[XACK] --> O[标记消息已处理]
P[XPENDING] --> Q[查看未确认消息]
end
Redis 集群 (Cluster)
Redis 集群提供数据分片和高可用性,支持自动故障转移。
集群架构
graph TD
subgraph "Redis集群"
subgraph "主节点"
A[Master1<br/>Slots: 0-5460]
B[Master2<br/>Slots: 5461-10922]
C[Master3<br/>Slots: 10923-16383]
end
subgraph "从节点"
D[Slave1] --> A
E[Slave2] --> B
F[Slave3] --> C
end
end
subgraph "客户端"
G[应用1] --> H[集群客户端]
I[应用2] --> H
H --> A
H --> B
H --> C
end
subgraph "故障转移"
J[节点监控] --> K[故障检测]
K --> L[选举新主节点]
L --> M[更新集群配置]
end
集群模式安装
# 创建集群配置文件模板
mkdir redis-cluster
cd redis-cluster
# 生成6个节点的配置文件(3主3从)
for port in 7000 7001 7002 7003 7004 7005; do
mkdir node-${port}
cat > node-${port}/redis.conf <<EOF
port ${port}
cluster-enabled yes
cluster-config-file nodes-${port}.conf
cluster-node-timeout 5000
appendonly yes
appendfilename "appendonly-${port}.aof"
dbfilename "dump-${port}.rdb"
logfile "redis-${port}.log"
daemonize yes
protected-mode no
EOF
done
# 启动所有节点
for port in 7000 7001 7002 7003 7004 7005; do
redis-server node-${port}/redis.conf
done
# 创建集群(Redis 5.0+)
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1
# 检查集群状态
redis-cli -p 7000 cluster info
redis-cli -p 7000 cluster nodes
# 连接集群
redis-cli -c -p 7000 # -c 启用集群模式
# 集群操作示例
redis-cli -c -p 7000
127.0.0.1:7000> set user:1001 "张三"
-> Redirected to slot [1935] located at 127.0.0.1:7000
OK
127.0.0.1:7000> set user:1002 "李四"
-> Redirected to slot [16383] located at 127.0.0.1:7002
OK
# 查看键的槽位信息
127.0.0.1:7000> cluster keyslot user:1001
(integer) 1935
# 添加新节点到集群
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
# 重新分片
redis-cli --cluster reshard 127.0.0.1:7000
# 删除节点
redis-cli --cluster del-node 127.0.0.1:7000 <node-id>
集群管理与监控
// 集群监控指标
const clusterMetrics = {
节点状态: "cluster_state, cluster_known_nodes",
槽位分布: "cluster_slots_assigned, cluster_slots_fail",
网络状态: "cluster_stats_messages_sent, cluster_stats_messages_received",
故障转移: "cluster_current_epoch, cluster_my_epoch",
性能指标: "operations_per_second, memory_usage, connection_count"
};
# 集群状态监控
redis-cli -p 7000 cluster info
redis-cli -p 7000 cluster nodes
redis-cli -p 7000 cluster slots
# 节点健康检查
redis-cli --cluster check 127.0.0.1:7000
# 集群修复
redis-cli --cluster fix 127.0.0.1:7000
# 备份集群配置
redis-cli --cluster backup 127.0.0.1:7000 /path/to/backup
# 集群性能测试
redis-cli --cluster call 127.0.0.1:7000 ping
redis-benchmark -c 50 -n 10000 -t set,get -p 7000
Redis 性能优化最佳实践
// Redis 性能优化建议
const performanceOptimization = {
内存优化: {
选择合适数据类型: "使用最节省内存的数据结构",
设置过期时间: "及时清理不需要的数据",
内存碎片整理: "定期执行MEMORY PURGE",
压缩配置: "启用适当的压缩算法"
},
网络优化: {
管道操作: "使用pipeline批量执行命令",
连接池: "复用连接避免频繁建立连接",
集群就近访问: "客户端连接最近的节点"
},
命令优化: {
避免大键: "拆分大的集合和哈希",
使用批量操作: "MGET, MSET代替多次GET, SET",
合理使用索引: "避免全量扫描操作"
}
};