Redis 全方位——不只是缓存 / Redis Beyond Caching
📅 创建时间:2026-05-08 🏷️ 标签:#Redis #KV #缓存 #分布式锁 #PubSub #Stream #RedisStack 📚 前置知识:[[00-db-overview]] [[01-mysql]](了解数据库基础概念) 📚 相关知识:[[06-kv-embedded]](etcd 对比)、[[08-vector-database]](Redis Vector Search)、[[/03-web/07-middleware/redis-guide/00-redis-overview]](Redis 全景)、[[/03-web/07-middleware/redis-guide/01-redis-architecture]](架构分析)、[[/03-web/07-middleware/redis-guide/02-redis-data-structures]](数据结构场景应用)、[[/03-web/07-middleware/redis-guide/03-redis-cache-patterns]](缓存三剑客)、[[/03-web/07-middleware/redis-guide/04-redis-distributed-locks]](分布式锁)、[[/03-web/07-middleware/redis-guide/05-redis-advanced-features]](高级特性)
Redis 定位速览
┌─────────────────────────────────────────────────────────────┐
│ Redis 在数据库版图中的位置 │
├─────────────────────────────────────────────────────────────┤
│ │
│ GitHub Stars: 67K+(redis/redis),内存数据库领域绝对霸主 │
│ 定位: 全功能内存 KV 存储,"从缓存到数据平台" │
│ 核心特征: 内存存储 + 丰富数据结构 + 持久化 + 集群 │
│ 适用场景: 缓存 / Session / 锁 / 队列 / 排行榜 / 实时数据 │
│ │
│ 一句话总结: │
│ Redis 几乎存在于每一个现代互联网系统的数据层中。 │
│ 它不只是一个缓存,它是一个多功能的内存数据平台。 │
│ │
└─────────────────────────────────────────────────────────────┘第1部分:五种核心数据结构
Redis 的强大之处在于它的"值"不只是字符串,而是丰富的数据结构:
┌─────────────────────────────────────────────────────────────┐
│ Redis 五种核心数据结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ String(字符串) │
│ 底层:动态字符串(SDS,Simple Dynamic String) │
│ 场景:缓存、计数器、Session、分布式锁值 │
│ │
│ Hash(哈希) │
│ 底层:字典(dict)+ 压缩列表(ziplist,小数据时) │
│ 场景:对象存储(优于 String 的 JSON)、配置缓存 │
│ │
│ List(列表) │
│ 底层:双向链表(adlist)+ 压缩列表(小数据时) │
│ 场景:消息队列、任务队列、最新消息流、时间线 │
│ │
│ Set(集合) │
│ 底层:哈希表(无序)+ IntSet(小整数时) │
│ 场景:标签、去重、共同好友、UV 统计 │
│ │
│ ZSet(有序集合) │
│ 底层:跳跃表(skiplist)+ 哈希表 │
│ 场景:排行榜、带权重的优先级队列、滑动窗口统计 │
│ │
└─────────────────────────────────────────────────────────────┘1.1 String
bash
# String 操作
SET user:1001:name "张三" # 写入
GET user:1001:name # 读取 → "张三"
MGET user:1001:name user:1002:name # 批量读取
SET counter:views 0 # 初始化计数器
INCR counter:views # 原子 +1 → 1
INCRBY counter:views 100 # 原子 +100 → 101
DECRBY counter:views 50 # 原子 -50 → 51
SET user:1001:session "token_abc123" EX 3600 # 写入 + 过期时间(1小时)
SETNX user:1001:lock "locked" # SET if Not eXists(分布式锁)
TTL user:1001:session # 查看剩余 TTL → 3542
# 分布式锁完整实现
SET lock:order:123 "worker_001" NX EX 30 # 获取锁
# ... 执行任务 ...
DEL lock:order:123 # 释放锁1.2 Hash
bash
# Hash 操作
HSET user:1001 name "张三" age "28" city "北京"
HGET user:1001 name # → "张三"
HMGET user:1001 name age city # → ["张三", "28", "北京"]
HGETALL user:1001 # → 所有字段
HINCRBY user:1001 login_count 1 # 计数器 +1
HSCAN user:1001 CURSOR 0 COUNT 10 # 游标遍历(大数据集)
# 实战:用户会话存储(Hash 优于 String)
HSET session:token_abc123
user_id 1001
username "zhangsan"
role "admin"
created_at "2026-05-08T10:00:00"
# 过期:对整个 key 生效,不支持单字段过期
EXPIRE session:token_abc123 36001.3 List
bash
# List 操作
LPUSH notifications:1001 "订单已发货" # 左侧插入
RPUSH notifications:1001 "系统升级公告" # 右侧插入
LRANGE notifications:1001 0 -1 # 获取所有(-1=末尾)
LLEN notifications:1001 # 列表长度
# 队列模式
LPUSH task:queue "job_001" # 生产者:入队
BRPOP task:queue 0 # 消费者:阻塞出队(0=永久等待)
# 最新消息流(类似 Twitter 时间线)
LPUSH timeline:1001 "新动态内容..."
LTRIM timeline:1001 0 99 # 只保留最新 100 条1.4 Set
bash
# Set 操作
SADD user:1001:tags "程序员" "羽毛球" "摄影" # 添加标签
SMEMBERS user:1001:tags # 获取所有标签
SISMEMBER user:1001:tags "羽毛球" # 是否存在 → 1
SCARD user:1001:tags # 标签数量 → 3
# 集合运算
SADD products:liked:keyboard "user_001" "user_002"
SADD products:liked:mouse "user_002" "user_003"
SUNION products:liked:keyboard products:liked:mouse # 并集
SINTER products:liked:keyboard products:liked:mouse # 交集 → ["user_002"]
SDIFF products:liked:keyboard products:liked:mouse # 差集 → ["user_001"]
# UV 统计(独立访客)
SADD page:view:2026-05-08 "user_001" "user_002" "user_003"
SADD page:view:2026-05-08 "user_002" "user_004"
SCARD page:view:2026-05-08 # → 4(去重后的 UV)1.5 ZSet(有序集合)
bash
# ZSet 操作(score = 排序分数)
ZADD leaderboard:posts 150 "post_001" # 帖子热度 +150
ZADD leaderboard:posts 89 "post_002"
ZADD leaderboard:posts 203 "post_003"
ZRANGE leaderboard:posts 0 9 WITHSCORES # 获取 Top 10(升序)
ZREVRANGE leaderboard:posts 0 9 WITHSCORES # 获取 Top 10(降序)
ZRANK leaderboard:posts "post_001" # 获取排名(0=第1名)
ZSCORE leaderboard:posts "post_001" # 获取分数 → 150
# 实战:实时排行榜(带时间衰减)
# 分数 = 基础分 + (时间衰减)
ZADD leaderboard:hourly
$(python -c "print(150 - (86400 - $(date +%s) % 86400) / 3600)")
"post_001"
# 滑动窗口统计(最近 N 分钟的请求量)
ZADD request:minute:202605081030 1 "req_001"
ZREMRANGEBYSCORE request:minute:-inf $(date +%s - 300) # 清理 5 分钟前
ZCARD request:minute:202605081030 # 当前分钟请求数第2部分:高级数据类型
2.1 HyperLogLog——UV 精确统计
bash
# HyperLogLog:用极低内存(12KB)做 UV 统计
# 标准误差 ~0.81%,适合大规模独立访客统计
PFADD page:uv:daily:2026-05-08 "user_001" "user_002" "user_003"
PFADD page:uv:daily:2026-05-08 "user_004"
PFADD page:uv:daily:2026-05-08 "user_002" # 重复用户,不计数
PFCOUNT page:uv:daily:2026-05-08 # → 4
# 合并多天 UV
PFMERGE page:uv:week page:uv:daily:2026-05-08 page:uv:daily:2026-05-07
PFCOUNT page:uv:week # → 7 天总 UV2.2 Bitmap——位图操作
bash
# Bitmap:用每个 bit 标记状态,节省 32 倍空间
# 适合:签到、在线状态、日期型计数器
# 用户签到(一年 365 天 = 365 bits = 46 bytes)
SETBIT user:1001:signin:2026 0 1 # 1月1日签到(偏移 0)
SETBIT user:1001:signin:2026 4 1 # 1月5日签到(偏移 4)
GETBIT user:1001:signin:2026 0 # → 1(已签到)
# 统计连续签到天数
BITCOUNT user:1001:signin:2026 # 总签到天数
BITOP AND last3days
user:1001:signin:2026:05:08
user:1001:signin:2026:05:07
user:1001:signin:2026:05:06
# 适合场景:DAU 统计、用户活跃天数2.3 Stream——消息队列
bash
# Stream:Redis 原生消息队列(替代 LPUSH/BRPOP 的更强大方案)
XADD mystream * sensor_id temp 23.5 # 生产消息(* = 自动 ID)
XADD mystream * sensor_id temp 24.1
XLEN mystream # 消息数量
# 消费者组(类似 Kafka Consumer Group)
XGROUP CREATE mystream mygroup $ MKSTREAM # 创建消费组,从新消息开始
XREADGROUP GROUP mygroup consumer1 STREAMS mystream ">" # 消费新消息
XREADGROUP GROUP mygroup consumer1 STREAMS mystream "0" # 消费未确认消息
# 确认处理
XACK mystream mygroup 1700000000000-0 # 确认消息已处理
XPENDING mystream mygroup # 查看未确认消息
# 范围查询
XRANGE mystream 1700000000000-0 1700000100000-0第3部分:持久化策略
┌─────────────────────────────────────────────────────────────┐
│ Redis 持久化策略 │
├─────────────────────────────────────────────────────────────┤
│ │
│ RDB(Redis Database): │
│ • 定时快照,生成 .rdb 文件 │
│ • fork() 子进程执行,不阻塞主进程 │
│ • 适合:备份、灾难恢复、数据冷备 │
│ • 缺点:可能丢失最后一次快照后的数据 │
│ │
│ AOF(Append Only File): │
│ • 每次写操作追加到 .aof 文件 │
│ • 三种同步策略: │
│ - always:每次写都 fsync(最安全,最慢) │
│ - everysec:每秒一次 fsync(默认,推荐) │
│ - no:由操作系统决定何时 fsync(最快,可能丢失多秒数据) │
│ • 适合:需要更高数据安全性的场景 │
│ • 缺点:文件体积大,恢复慢 │
│ │
│ 混合持久化(RDB + AOF,推荐): │
│ • AOF 文件以 RDB 格式开头(重写时) │
│ • 同时获得 RDB 的快速恢复 + AOF 的数据完整性 │
│ • 配置:aof-use-rdb-preamble yes │
│ │
│ 建议: │
│ • 开发/测试:RDB 或不用持久化 │
│ • 生产:混合持久化 + AOF everysec │
│ │
└─────────────────────────────────────────────────────────────┘第4部分:Redis Cluster
┌─────────────────────────────────────────────────────────────┐
│ Redis Cluster 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 16384 个 slot(槽),均匀分布在多个节点上 │
│ │
│ 客户端 ──自动路由──┐ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Redis Cluster │ │
│ │ │ │
│ │ Slot 0-5460 ────▶ Node-1 (主) │
│ │ │ ↕ 副本 │
│ │ │ Node-1-Slave │
│ │ Slot 5461-10922 ──▶ Node-2 (主) │
│ │ │ ↕ 副本 │
│ │ │ Node-2-Slave │
│ │ Slot 10923-16383 ──▶ Node-3 (主) │
│ │ │ ↕ 副本 │
│ │ │ Node-3-Slave │
│ └────────────────────────┘ │
│ │
│ 特点: │
│ • 自动分片(数据按 slot 分布) │
│ • 自动故障转移(主节点宕机 → 从节点升主) │
│ • 无中心架构(客户端直连各节点) │
│ • 槽迁移时集群可用(但不保证原子性) │
│ • 限制:多 key 操作要求 key 在同一节点(使用 hash tag) │
│ │
│ Hash Tag(让相关数据落在同一节点): │
│ • key = "user:{1001}:profile" 和 "user:{1001}:orders" │
│ • {1001} 是 hash tag,slot 计算只看 {1001} │
│ │
└─────────────────────────────────────────────────────────────┘第5部分:Redis Sentinel(哨兵)
┌─────────────────────────────────────────────────────────────┐
│ Redis Sentinel 哨兵架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 应用客户端 │
│ │ │
│ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Sentinel 1│ │Sentinel 2│ │Sentinel 3│ ← 哨兵集群 │
│ │(Leader) │ │ │ │ │ (奇数个) │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Master (主节点) │ │
│ └──────────┬──────────┘ │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Slave 1 │ │ Slave 2 │ │ Slave 3 │ ← 从节点 │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 哨兵职责: │
│ • 监控:检查主从节点是否存活 │
│ • 通知:节点宕机时通知客户端 │
│ • 自动故障转移:主节点宕机 → 投票选主 → 从升主 │
│ • 提供配置:告诉客户端当前主节点地址 │
│ │
│ 客户端连接方式: │
│ JedisSentinelPool / StackExchange.Redis / Redis-py │
│ │
└─────────────────────────────────────────────────────────────┘第6部分:Redisson 实战
Redisson 是 Java 中最流行的 Redis 客户端,封装了大量分布式数据结构:
java
// Redisson: 分布式数据结构
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RRateLimiter;
RedissonClient redisson = Redisson.create();
// ================== 分布式锁 ==================
RLock lock = redisson.getLock("order:123");
try {
// 尝试获取锁(等 10 秒,锁自动 30 秒过期)
boolean acquired = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (acquired) {
// 执行业务逻辑
processOrder(123);
}
} finally {
lock.unlock(); // 释放锁
}
// ================== 分布式 Map ==================
RMap<String, User> userMap = redisson.getMap("users");
// 相当于 ConcurrentMap + Redis 持久化 + 过期时间
userMap.put("1001", new User("张三", 28));
User u = userMap.get("1001");
// ================== 限流器 ==================
RRateLimiter limiter = redisson.getRateLimiter("api:rate:limit");
// 每秒允许 100 个请求
limiter.setRate(RateType.OVERALL, 100, 1, RateIntervalUnit.SECONDS);
boolean allowed = limiter.tryAcquire(1); // 请求一个令牌
if (!allowed) {
throw new RateLimitException("请求过于频繁");
}
// ================== 分布式信号量 ==================
RSemaphore semaphore = redisson.getSemaphore("resource:pool");
semaphore.trySetPermits(10); // 设置资源池大小
semaphore.acquire(); // 获取一个资源
// ... 使用资源 ...
semaphore.release(); // 释放资源
// ================== 分布式队列 ==================
RBlockingQueue<Task> queue = redisson.getBlockingQueue("task:queue");
Task task = queue.poll(10, TimeUnit.SECONDS); // 阻塞获取
queue.put(new Task("job_001")); // 入队第7部分:Redis 在 LLM/Agent 中的应用
┌─────────────────────────────────────────────────────────────┐
│ Redis 在 AI 应用中的关键角色 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. LLM 响应缓存(语义缓存) │ │
│ │ key = MD5(prompt + model + temperature) │ │
│ │ value = LLM 响应结果 │ │
│ │ → 相同问题避免重复调用,节省 30-70% API 费用 │ │
│ │ → LangCache(Redis 官方 LLM 缓存方案) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 2. 会话状态存储 │ │
│ │ Hash: session:{id} │ │
│ │ → messages: [历史消息 JSON] │ │
│ │ → context_tokens: 当前上下文 token 数 │ │
│ │ → last_active: 最后活跃时间 │ │
│ │ → 分布式环境下跨服务器共享 Session │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 3. 流式输出缓冲 │ │
│ │ Stream: llm:stream:{session_id} │ │
│ │ → WebSocket 多端同步(手机 + 电脑看到同一个回答) │ │
│ │ → SSE 断线重连续传 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 4. Agent 任务队列 │ │
│ │ List: agent:tasks:pending │ │
│ │ → 长时间任务异步执行,前端轮询状态 │ │
│ │ → 优先级队列(ZSet: score=priority) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 5. Token 速率限制 │ │
│ │ ZSet: ratelimit:{user_id}:{minute} │ │
│ │ → 按时间窗口统计 Token 使用量,超额限流 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘学习状态:🟡 开始学习