482 words
2 minutes
my own enterprise RAG double cache

原有query查询分析

Query: "Token管理"
├─ Query Expansion → 5 sub-queries (Token管理, Token, JWT, ...)
├─ For EACH sub-query:
│ ├─ DashScope embed() ← 网络 I/O, 慢
│ ├─ PostgreSQL BM25 FTS ← DB I/O
│ └─ PostgreSQL vector sim ← DB I/O
├─ RRF Fusion ← 快
└─ DashScope rerank() ← 网络 I/O, 慢

缓存策略分层

请求
┌─────────────────────┐
│ L1: Query Result Cache │ ← 相同 query 直接返回 (TTL 5min)
│ (In-memory LRU) │
└─────────────────────┘
│ miss
┌─────────────────────┐
│ L2: Embedding Cache │ ← 相同 text → embedding (持久化)
│ (SQLite / Redis) │
└─────────────────────┘
│ miss
┌─────────────────────┐
│ L3: BM25 Index Cache │ ← 启动时加载, 增量更新
│ (In-memory) │
└─────────────────────┘
DashScope API + PostgreSQL (pgvector + FTS)

各层详解

L1: Query Result Cache(命中率最高)

  • 缓存键:skill_point + top_k → RetrievalResult
  • TTL:5 分钟(可配置)
  • 淘汰:LRU,1000 条上限
  • 命中场景:用户反复查询同一个技能点

L2: Embedding Cache(减少 DashScope 调用)

L3: BM25 Index(消除 DB BM25 查询延迟)

  • 启动时从 PostgreSQL 加载全量文本,构建内存 BM25 索引
  • 增量更新:文档变更时只更新对应部分,不全量重建
  • 或更简单:每次 build_index.py 后将 BM25 数据序列化到文件,API 启动时反序列化

最简可行方案:L1 + L3

L2(embedding 缓存)实际上可以简化——因为 build_index.py 已经把 embedding 存进 pgvector 了,search_similar_chunks 直接查 pgvector 就行。

只加 L1(Query Result Cache)和 L3(BM25 Index):
启动时:
├─ 从 pgvector 加载所有 chunk embeddings → HybridRetriever
└─ 从 PostgreSQL 加载所有 chunk 文本 → BM25 index
请求时:
├─ Check L1 cache → hit? return
├─ Query Expansion
├─ BM25 (in-memory, 快)
├─ Vector search (pgvector, 已有索引)
├─ RRF + Rerank
└─ Store result in L1 cache

性能提升:

  • BM25 FTS 查询:从 pgvector round-trip 变成 内存操作,快 10-100x
  • Query 结果缓存:重复查询 零数据库调用
my own enterprise RAG double cache
https://sgjki547.top/posts/rag-multi-cache/
Author
SGJki
Published at
2026-04-20
License
CC BY-NC-SA 4.0