Redis 在大模型应用中有哪些用途?缓存、会话与限流
码海寻道 · 大模型、智能体与 RAG 工程组件系列第 29 篇
大模型应用通常比普通 CRUD 服务更容易遇到高延迟、高成本和突发流量:一次回答可能要调用 Embedding、向量数据库、Reranker 和大模型。Redis 可以把热点数据、会话状态和限流计数放到内存中,减少重复计算并保护后端服务。
Redis 在这里更像"加速器和保护层",不是 PostgreSQL、对象存储或 Milvus 的事实源。缓存键必须包含租户、用户、模型、知识库版本和权限范围等影响结果的维度,否则很容易把一个用户的结果返回给另一个用户。
一、Redis 在架构中的位置
text
用户请求
↓
API 服务
├── Redis:缓存、会话、限流、短期状态
├── PostgreSQL:业务事实和审计
├── Milvus:向量检索
└── 大模型 API:生成答案
Redis 不是 PostgreSQL 的永久替代品,也不是消息队列、对象存储和向量数据库的万能替代品。它适合保存可以快速读取、允许设置过期时间或需要高并发访问的数据。
二、用途一:缓存 Embedding 和检索结果
相同问题或相同文档内容可能被重复处理,可以缓存:
- 文本的 Embedding;
- 问题改写结果;
- RAG 召回结果;
- 热门问题的最终答案;
- 模型配置和权限范围的短期结果。
缓存键必须包含影响结果的关键信息:
text
rag:answer:{tenant_id}:{knowledge_version}:{model_version}:{query_hash}
如果只使用问题文本作为 Key,知识库更新、租户切换或模型升级后可能返回旧答案。
三、Cache-Aside 基本模式
python
value = redis.get(cache_key)
if value is not None:
return json.loads(value)
value = expensive_rag_query()
redis.set(cache_key, json.dumps(value), ex=300)
return value
缓存未命中时读取真实数据,再写入 Redis。生产实现还要处理并发击穿、异常不缓存、序列化版本和缓存大小。
四、用途二:保存会话和短期记忆
HTTP 请求本身无状态,多实例部署时不能把会话只放在某个应用进程内。Redis 可以保存:
- 登录会话和 Token 状态;
- 多轮对话的最近消息;
- Agent 当前执行上下文;
- 文件解析任务的临时状态;
- 短期用户偏好。
示例:
python
redis.hset(
f"chat:session:{session_id}",
mapping={
"user_id": user_id,
"messages": json.dumps(messages, ensure_ascii=False),
},
)
redis.expire(f"chat:session:{session_id}", 3600)
对话历史不应无限增长。应设置消息数量、Token 预算和过期时间,长期记忆则应经过筛选后进入 PostgreSQL 或专门的记忆存储。
五、用途三:接口限流
大模型调用成本高,必须防止单个用户、租户或 IP 突发消耗资源。Redis 适合实现固定窗口、滑动窗口和令牌桶等限流算法。
一个简单的固定窗口示例:
python
key = f"rate:{tenant_id}:{int(time.time()) // 60}"
count = redis.incr(key)
if count == 1:
redis.expire(key, 60)
if count > 100:
raise TooManyRequests()
这个示例容易理解,但窗口边界可能出现突刺。严格限流应使用 Lua 脚本或成熟组件保证计数与过期设置的原子性。
六、缓存一致性怎么处理?
缓存最大的问题不是读不到,而是读到旧数据。常见策略:
TTL
让缓存自动过期,适合可接受短暂陈旧的结果。
主动删除
文档发布、权限变化或模型升级时删除相关 Key。
版本化 Key
把知识库版本和模型版本放入 Key,让新版本自然使用新缓存。
事件失效
业务数据变化时发布事件,由缓存消费者删除或刷新对应 Key。
对于 RAG 结果缓存,推荐把知识库版本放进 Key:
text
rag:{tenant_id}:{kb_id}:{serving_version}:{query_hash}:{model_version}
文档发布时切换 serving_version,新请求自然使用新 Key,旧缓存可以异步过期。不要只依赖删除所有旧 Key,因为大规模模糊删除可能阻塞 Redis 或误删其他租户数据。
七、缓存穿透、击穿和雪崩
穿透
请求查询一个根本不存在的数据。可以缓存空结果,但要设置较短 TTL,并防止用户输入直接构造任意 Key。
击穿
热点 Key 同时过期,大量请求一起访问后端。可以使用互斥锁、单飞机制或提前刷新。
雪崩
大量 Key 在同一时间过期。设置 TTL 随机抖动,并分批预热和删除。
八、Redis 不是最终事实来源
以下数据不要只存 Redis:
- 账单和计费记录;
- 审计日志;
- 文档发布状态;
- 权限策略;
- 不可恢复的任务结果。
还要明确 Redis 故障时的降级路径:Embedding 缓存失效后重新计算,检索缓存失效后回源 Milvus/pgvector,会话缓存失效后从 PostgreSQL 恢复或要求重新建立会话。降级逻辑应设置超时和并发上限,避免 Redis 故障把压力瞬间传导给所有下游。
Redis 可以保存访问加速所需的副本,但关键事实仍应写入持久化数据库或对象存储。
九、上线检查清单
- Key 包含租户、版本和模型等必要维度;
- 所有缓存都有合理 TTL 或失效策略;
- 缓存命中不会绕过权限校验;
- 限流计数具有原子性;
- 大对象不会无限写入 Redis;
- Redis 故障时有降级或明确失败策略;
- 关键数据不会只存在 Redis;
- 监控命中率、内存、连接数和淘汰数量。
- 已设置最大内存、淘汰策略和高峰期降级方案;
- Redis Streams 若承载任务,已设计 ACK、Pending 和重领机制;
结语
Redis 在大模型应用中最常见的角色是"加速器和保护层":缓存减少重复调用,会话支撑多实例协作,限流控制成本和风险。真正可靠的设计,还要同时考虑失效、一致性、降级和持久化事实来源。
下一篇将讨论 RAG 原始文件应该放在哪里,以及 MinIO、S3 和 OSS 的职责差异。
参考资料
本文为"码海寻道"原创技术文章。缓存策略需要结合业务数据新鲜度、成本和故障降级要求设计。
