个人主页: > for_ever_love__ <(欢迎各位大佬莅临😊)
其他栏目: > 大模型开发从0到1 <
其他栏目: > iOS项目总结大全 <
其他栏目: > 我想学python了 <
其他栏目: > iOS UI <
文章目录
- 增量索引实战:新文档怎么做到分钟级可检索
-
- 一、增量索引的三个基本操作
- 二、变更侦测:怎么知道什么变了
-
-
- [2.1 轮询比对(最通用)](#2.1 轮询比对(最通用))
- [2.2 消息驱动(有事件源的场景)](#2.2 消息驱动(有事件源的场景))
-
- [三、增量更新的实现:upsert 的正确姿势](#三、增量更新的实现:upsert 的正确姿势)
-
- [3.1 块 ID 稳定设计](#3.1 块 ID 稳定设计)
- [3.2 删旧查新必须原子](#3.2 删旧查新必须原子)
- [3.3 向量库的 Upsert 语义](#3.3 向量库的 Upsert 语义)
- 四、删除与权限同步:安全红线
- 五、完整流水线设计
- 六、一致性级别:没必要全上强一致
- 七、监控指标
- 八、落地检查清单
- 九、小结
增量索引实战:新文档怎么做到分钟级可检索
"合同昨天就存进系统了,为什么今天问它还说不知道?"------RAG 系统上线后最高频的用户抱怨。全量重建索引(几十万文档重新 embedding)一次几小时,不可能每天做;不管它,知识库就永远是几天前的快照。增量索引要解决的就是这个矛盾:新文档/变更文档快速进索引,旧索引不受扰动,删除要同步生效。这篇文章讲增量索引的完整机制:变更侦测、局部更新、删除同步、一致性保证,以及一套可落地的流水线。
一、增量索引的三个基本操作
先把"增量"拆开------它从来不是一个操作,是三个:
- 新增:新文档 → 解析 → 切块 → embedding → 入库。最简单,也是 90% 的日常流量;
- 更新 :文档内容变了 → 旧的块要删、新的块要加。比新增难一个量级------找旧块、删干净、加新的,中间状态要可控;
- 删除 :文档下架/权限收回 → 索引里的块要消失,且不能残留------漏删一条就是安全事故(离职员工合同还在被检索)。
三个操作的正确性要求不同:新增可以最终一致(慢几分钟没事),更新和删除必须可追踪、可回滚、无残留。
二、变更侦测:怎么知道什么变了
2.1 轮询比对(最通用)
定时任务扫描文档源(对象存储、CMS、数据库),和本地记录的"已处理清单"比对:
python
# 已处理清单:doc_id → {version, etag, processed_at}
processed = load_state()
for doc in scan_source(): # 定期全扫(或按 mtime 增量扫)
key = (doc.id, doc.version) # version/etag 由源给出
if processed.get(doc.id) != key:
enqueue("upsert", doc)
for doc_id in set(processed) - set(doc.id for doc in scan_source()):
enqueue("delete", doc_id) # 源里没了的 = 删除
关键字段 :version/etag(源文件的版本标识)必须有------不能只比 mtime(批量复制会全刷 mtime,造成无意义全量更新)。对象存储的 etag(MD5)、CMS 的 version 字段、数据库的 updated_at 都行。
删除侦测的坑 :全扫才能发现删除------如果源支持增量列举(只列变更),删除事件容易漏。没有删除事件的源,就必须全量比对 ID 集合,这是成本换正确性。
2.2 消息驱动(有事件源的场景)
Confluence webhook、Kafka 消息、S3 事件通知------源系统有事件推送,比轮询实时(秒级 vs 分钟级)且省扫描成本。适合自研系统(发布/更新文章时主动发消息)。混合架构最务实:消息驱动做主链路(快),定时轮询兜底(防消息丢失的删除侦测)。
三、增量更新的实现:upsert 的正确姿势
单个文档的 upsert 流程:
新文档进来
├─ 1. 内容哈希:hash(doc 正文) → 和已记录的比,没变直接跳过(幂等)
├─ 2. 解析 → 清洗 → 切块 → 生成块级 ID = hash(doc_id + chunk_index + chunk_text)
├─ 3. 删旧:删除该 doc_id 的所有旧块(按 doc_id 前缀删 / 按 where 过滤删)
├─ 4. 插新:新块批量入库
└─ 5. 记录状态:doc_id → version/etag/块数/chunk_ids
3.1 块 ID 稳定设计
块 ID 用 hash(doc_id + chunk_index + chunk_hash) 生成有两个好处:内容没变的块 ID 不变 (去重、后续可做细粒度 diff),重新解析后一致(幂等重跑不产生垃圾块)。
3.2 删旧查新必须原子
删除旧块和插入新块之间有窗口:检索恰好发生在这个窗口,可能命中"半新半旧"的状态。两种处理:
- 简单场景(容忍短暂不一致):先删后插,窗口几十毫秒,用户无感;
- 严格场景 :新块用新 doc_version 写入(同一 doc_id 下并存 old/new 两组块),写完后原子切换"生效版本"标记,再异步删旧块------这就是小型的版本化索引(下一篇 rg5-index-version 展开)。
3.3 向量库的 Upsert 语义
主流向量库都支持按 ID upsert(相同 ID 覆盖写):Qdrant upsert、Milvus upsert、Weaviate merge。用 upsert 而非 insert------先 insert 后 delete 的写法在失败时会留孤儿块。
按文档删除要高效 :向量库按 ID 删是点删,删一篇文档的 50 个 chunk = 50 次调用。优化:metadata 里带 doc_id,用过滤删除一次清完(delete where doc_id = 'X',Qdrant/Milvus 2.3+ 都支持过滤删除)。
四、删除与权限同步:安全红线
删除是最容易被做漏的操作,漏一片就是合规事故。要点:
- 删除是全链路传播:文档从 CMS 消失 → 索引删块 → 图谱删实体(带血缘来源的边清理)→ 缓存清除(该文档的摘要/embedding 缓存)→ 引用它的缓存失效。漏一环,删了也白删;
- 权限变更等同删除 :文档权限从"全员可见"改成"部门可见",索引里 ACL 元数据要更新------ACL 元数据延迟 = 越权可见窗口;
- 删除要可验证:删完跑一次"按 doc_id 查存量"断言,残留数必须为 0,不为 0 报警;
- 软删缓冲 :正式删前标记
deleted_at,24 小时缓冲期(源系统误删可回滚),缓冲期后物理删------防手滑,防误操作传播。
五、完整流水线设计
文档源(CMS/对象存储/DB)
│
├─ 事件流(webhook/MQ)─┐
└─ 定时轮询(兜底+删侦测)┘
↓
变更队列(upsert/delete,按 doc_id 保序)
↓
处理器:
├─ upsert: hash 去重 → 解析 → 切块 → embedding → upsert 入库
├─ delete: 过滤删除 + 缓存清除 + 断言残留为 0
└─ 每步 checkpoint 落库
↓
状态库(doc_id → version/块数/状态)
↓
监控:队列堆积、处理延迟、删除残留、解析失败率
工程要点:
- 保序:同一 doc_id 的 upsert 和 delete 要按时间序处理(先删后加的乱序 = 数据丢失);
- 并发:不同 doc 并行,同 doc 串行(per-key 队列);
- 幂等:全链路用内容 hash + 稳定 ID,任何一步失败重跑不产生重复(前篇图谱流水线的 checkpoint 设计在这里同样适用);
- 背压 :embedding API 限流时队列会涨,监控堆积深度,超阈值告警------知识库延迟 RAG 用户最先感知。
六、一致性级别:没必要全上强一致
不同业务对延迟的容忍度不同,别过度设计:
| 业务 | 要求 | 方案 |
|---|---|---|
| 客服知识库 | 分钟级可查 | 轮询 5 分钟 + upsert 直接生效 |
| 内部文档搜索 | 小时级可接受 | 每日批量增量 |
| 金融合规库 | 权限变更秒级 | 消息驱动 + ACL 实时刷 + 断言校验 |
| 合同库 | 删除必须零残留 | 软删缓冲 + 全链路传播 + 残留断言 |
设计原则:按业务定一致性级别,别为技术优雅上最终一致------反过来也一样,分钟级能用的别承诺"实时"。
七、监控指标
增量索引的健康度全看这四个指标:
- 端到端延迟:文档创建 → 可检索的耗时(P50/P99,按业务定阈值);
- 队列深度与处理速率:堆积持续上涨 = 消费能力不足,扩容 embedding 并发;
- 删除残留数:每天全量校验一次(已删文档 ID 抽样查索引),> 0 立即告警;
- 失败率:解析失败、embedding 失败、写入失败,按原因分类聚合作业看板。
这四个指标做成 Dashboard,RAG 知识库从"黑盒"变"可运维系统"------索引延迟和删除残留是 RAG 独有的 SLO,传统搜索系统同样要盯。
八、落地检查清单
- 变更侦测:源有事件走消息,无事件轮询 5~15 分钟,删除侦测必须全扫比对;
- upsert:内容 hash 去重 + 块 ID 稳定 + 删除旧块与插入新块的窗口控制;
- 删除:全链路传播(索引/图谱/缓存)、软删缓冲 24h、残留断言为 0;
- 权限变更等同 upsert:ACL 元数据实时更新,延迟进监控;
- 流水线:per-doc 保序 + 跨 doc 并行 + 幂等重跑;
- 状态库 + checkpoint 落库,中断可续跑;
- 四个监控指标:端到端延迟、队列深度、删除残留、失败率;
- 一致性级别按业务定,写进 SLO 文档。
九、小结
增量索引的四句话:
- 增量 = 新增 + 更新 + 删除三个操作,更新和删除的复杂度远高于新增,全链路传播(索引/图谱/缓存/ACL)一步不能漏;
- 侦测靠消息驱动 + 定时轮询兜底,轮询必须比对全量 ID 集合才能发现删除,内容 hash 决定幂等去重;
- 删除是安全红线:软删缓冲、残留断言、权限变更等同删除------漏一条就是合规事故;
- RAG 知识库要有自己的 SLO:索引延迟、队列深度、删除残留、失败率,四个指标让系统可运维。
下一篇讲索引版本管理:怎么做到"重建索引不中断服务",蓝绿切换在 RAG 里的做法。