RAG分页检索,海量知识库分片查询,避免一次加载过多文档
当知识库里的文档达到几十万甚至上百万条时,RAG 系统如果一次性把所有文档加载到内存里做向量化或召回,轻则查询变慢,重则直接把服务拖崩。
RAG 分页检索的思路是每次只读取一个小批次,配合分片查询把海量数据拆成多个区间处理,从源头避免内存和性能压力。
本文就用一个零基础能跟上的方式,讲清适用场景、实现方法和常见坑。
什么情况下必须做分页检索和分片查询
如果你的 RAG 服务出现以下现象,就说明需要引入分页检索或分片查询了:
- 启动或建索引时内存占用持续暴涨,甚至触发 OOM。
- 用户问一个问题,系统要把整个知识库扫一遍才能返回结果。
- 批量导入文档时,一次性读取全部内容导致接口超时。
- 向量数据库在查询时因为返回数量过大而响应缓慢。
分页解决的是“一次不要取太多结果”的问题,分片解决的是“如何把大任务拆成小任务并行处理”的问题。
两者配合,才能让海量知识库稳定运行。
环境准备与基础思路
动手之前,先确认你的 RAG 项目里已经安装了向量数据库客户端,比如 Chroma、FAISS、Elasticsearch 或 Milvus。
代码层面只需要确保能执行两件事:
- 支持带偏移量的查询(或基于游标的查询)。
- 支持按某个字段做条件过滤。
下面以 Python 伪代码演示,实际使用时把函数名替换成你所用的数据库 API 即可。
核心思路是先定义每批读取的条数,再用循环逐批处理,直到全部读完。
分页检索的两种常用写法
最简单的写法是 limit + offset,适合数据量在十万级以内的场景:
batch_size = 100
offset = 0
while True:
# 假设这是你的向量数据库查询接口
docs = query_knowledge_db(collection="knowledge", limit=batch_size, offset=offset)
if not docs:
break
process_docs(docs) # 做向量化、回答或过滤
offset += batch_size
这段代码每次只取 100 条,处理完后再取下一批。offset 不断累加,直到返回为空。
但 offset 越大,数据库需要跳过的行就越多,当偏移量到几万时性能会明显下降。
更推荐用游标分页,很多数据库支持按 ID 或时间戳范围定位:
last_id = None
batch_size = 100
while True:
if last_id is None:
docs = query_knowledge_db(collection="knowledge", limit=batch_size)
else:
docs = query_knowledge_db(collection="knowledge", limit=batch_size, after=last_id)
if not docs:
break
process_docs(docs)
last_id = docs[-1]["id"] # 每批取最后一条的ID作为下一批起点
游标分页不存在“跳过大段数据”的成本,深层分页时性能更稳定。
如果你的向量库支持 after 或 search_after 参数,优先用这种方式。
海量知识库分片查询策略
分页解决了单次读取量的问题,但全表扫描本身也很慢。
分片查询可以把知识库切成多个互不重叠的区间,比如按文档 ID 范围、按创建时间、按文档类型。
下面是一个按 ID 区间分片的示例:
start_id = 0
batch_size = 5000
max_id = get_max_id("docs")
while start_id < max_id:
end_id = min(start_id + batch_size, max_id)
docs = query_knowledge_db("SELECT * FROM docs WHERE id >= ? AND id < ?", (start_id, end_id))
process_docs(docs)
start_id = end_id
按时间分片同理,比如按月份处理:
for month in ["2024-01", "2024-02", "2024-03"]:
docs = query_knowledge_db(collection="knowledge", filter={"month": month})
process_docs(docs)
分片粒度建议根据单批次处理耗时来定。
如果一批 5000 条处理要 5 秒,说明粒度偏大,可以适当减小;
如果处理太快,可能是粒度太小导致循环次数过多。
避坑与验证方法
实操中这几个问题最容易踩,提前注意能省不少时间:
- 不要无脑调大
limit。一次加载多看起来效率高,但内存峰值会成倍上涨,尤其是需要把文档内容转换成向量时。合理值建议从 64 或 128 开始测试。 - 注意数据一致性。如果分片查询期间知识库还在写入新文档,可能造成重复或漏读。建议先暂停写入,或者按快照时间过滤。
- 分页参数别用 float。很多数据库要求整数,Python 中
limit=100.0会直接报错,先用int()包一层。 - 超时时间要留够。批量处理比单条查询慢,接口和客户端都要把超时调大,比如从 10 秒改成 60 秒。
验证分页和分片是否生效,重点看三个指标:
- 进程内存占用是否保持平稳,不会随扫描进度持续飙升。
- 每次处理完一批后,内存曲线会回落到接近初始值。
- 同一批数据重复处理时,结果数量一致,没有遗漏或重复。
最简单的方式是在处理函数里打印 batch_index 和 len(docs),跑到最后一批时确认总条数等于知识库总数。
如果你正在处理 RAG 分页检索和知识库分片查询,建议先按本文的循环模式写一版最小示例,用 1 万条测试数据跑通,再逐步扩大到全量。
遇到性能下降时,优先检查是不是分页深度太大导致扫描变慢,必要时换成游标分页或缩小分片粒度,多数问题都能在不动架构的情况下解决。