最近看到大家在谈论RAG,正好我也分享一下踩过的坑,今天谈论一下rag比较核心的一环,索引重建与向量库同步
Demo 只需要跑通链路,生产系统必须回答:文档更新了怎么办?索引失败一半怎么办?用户会不会查到半成品?
这个问题可以先从正常链路说起。
大多数 RAG 的入库链路都长这样:
| 上传文档 | → | 解析文本 | → | 切 chunk |
| ↓ | ↓ | ↓ | ||
| 生成 embedding | → | 写向量库 | → | 写关键词索引 |
问答时再从向量库 / 关键词索引里召回 chunk。
结合当前项目,链路基本也是这样:
DocumentApplicationService.triggerIngestion → 从 MinIO 读取原始文件 → DocumentParserPort 解析文本 → DocumentIngestionDomainService 切 chunk → kb_document_chunk 保存 chunk 正文 → EmbeddingPort 生成向量 → MilvusVectorSearchAdapter 写向量 → ElasticsearchKeywordSearchAdapter 写关键词索引 → 文档状态变成 COMPLETED这套链路作为 Demo 没问题。
它能说明 RAG 入库的基本过程。但如果拿它回答“文档更新后怎么重建索引,如何保证向量库同步”,就明显不够了。
一、上述链路的问题
- 它只解决第一次入库
第一次上传文档时,解析、切块、向量化、写索引都很顺。但生产里文档会更新,解析策略会升级,chunk 规则会变化。这个时候不是“再跑一遍”这么简单,而是要处理旧索引和新索引的关系。
- retry 不是 rebuild
Demo 里常见一个误区:失败了就 retry,更新了也 retry。
| retry | 上一次任务失败了,继续补救。 |
| rebuild | 当前已有索引,但我要基于新内容或新策略生成一套新索引。 |
如果把 rebuild 做成 retry,很容易出现:旧索引被删了,新索引又因为状态不允许或中途失败没建起来。
- 删除旧索引后再写新索引,会有空窗期
删除旧 MySQL chunk 删除旧 Milvus vector 删除旧 ES document 重新切 chunk 重新 embedding 重新写索引这个做法简单,但生产风险很大。一旦新索引构建失败,旧索引已经没了,用户就查不到内容。
- 不删除旧索引,又会新旧混查
如果不删旧索引,直接写新索引,也会出问题。同一个知识库里可能同时存在旧 chunk 的向量、新 chunk 的向量、旧 ES 文档、新 ES 文档。
如果查询只按knowledgeBaseId过滤,就可能把旧内容召回。
- chunk 策略变化,本质是索引版本变化
比如原来按 800 字符切,现在改成按 Markdown 标题树切。这不是参数微调,而是索引结构变化。因为 chunk 的数量、边界、内容、顺序都变了,embedding 输入也变了,旧向量就不能再当作同一套索引使用。
- embedding 模型变化也必须重建
比如从 1536 维模型换到bge-m31024 维模型。旧向量和新向量不能混在一起比较,embedding 模型和维度必须成为索引版本的一部分。
- MySQL、Milvus、ES 不可能靠强事务同步
MySQL chunk 写成功 Milvus 写成功一半 ES bulk 部分失败真实系统里经常出现这种半成功。如果没有版本和对账机制,你很难知道当前索引到底是不是完整的。
二、生产级应该怎么做
真正生产级的答案不是“删除旧索引再重建”。
MySQL 做真相源 Milvus / ES 做派生索引 索引版本化 新版本后台构建 构建成功后切 active_version 旧版本延迟清理 通过对账保证最终一致核心是四个词:
| 版本化 | 蓝绿切换 |
| 幂等任务 | 对账修复 |
我把这套方案画成了一张图:
1. 引入 document_revision
文档不要覆盖更新,而是生成版本。
document_id = doc_001 revision_id = r1 / r2 / r3每次文档内容变化,就生成新的 revision。这样系统才能知道:当前检索的是文档哪个版本、这个 chunk 来自哪次文档修改、新版本有问题时能不能回滚。
2. 引入 index_version
索引也要有版本。
index_version = v1 / v2 / v3只要下面任何东西变化,都应该生成新的 index_version:
| 文档内容变化 | 必须重建 |
| chunk 切分策略变化 | 必须重建 |
| Markdown / PDF 解析策略变化 | 必须重建 |
| 知识库树结构变化 | 通常要重建受影响范围 |
| embedding 模型 / 维度变化 | 必须重建,不能混查 |
也就是说,重建索引不是覆盖旧索引,而是构建一个新的索引版本。
3. 知识库维护 active_index_version
知识库应该记录当前线上生效的索引版本:
active_index_version = v3 indexing_version = v4含义是:
v3 正在线上服务 v4 正在后台构建查询时永远只查:
knowledge_base_id = ? index_version = active_index_version status = ACTIVE这样即使 v4 还在构建,用户也不会查到半成品。
4. Milvus 和 ES 都要带 index_version
Milvus 里的 vector metadata 至少要带:
chunk_id document_id knowledge_base_id revision_id index_version statusES 文档也一样。不能只按knowledgeBaseId查,只按知识库查,旧向量迟早会混进来。
5. 用蓝绿方式重建索引
生产级重建流程应该是:
1)创建新的 index_version,例如 v4。
2)后台用新策略解析、切 chunk、生成 embedding。
3)新 chunk 写 MySQL,状态 BUILDING。
4)新 vector 写 Milvus,带 index_version = v4。
5)新 keyword doc 写 ES,带 index_version = v4。
6)对账 MySQL / Milvus / ES 是否完整。
7)校验通过后,把 v4 标记 ACTIVE。
8)原子切换 knowledge_base.active_index_version = v4。
9)旧版本 v3 延迟清理。
整个过程中,线上仍然使用 v3。只有 v4 完整构建成功,才切过去。这就是索引层面的蓝绿发布。
6. 任务必须幂等
索引任务一定会失败,也一定会重试。所以同一个:
document_revision index_version chunk_index content_hash应该生成稳定的 chunkId / vectorId。否则每次重试都会制造一批新向量,最后向量库里全是重复数据。
7. 必须有对账任务
不要相信日志说“写入成功”。生产里必须定期对账:
MySQL active chunks 数量 Milvus active vectors 数量 ES active docs 数量 缺失多少 多余多少 旧版本残留多少 hash 是否一致对账结果决定后续动作:
| 缺的 | 补写 |
| 错的 | 重写 |
| 多的 | 删除 |
| 旧版本 | 延迟 GC |
这才叫保证向量库同步。
三、如果是当前 Demo,最低怎么补?
如果只是当前这个学习项目,不想一开始就做完整版本化,可以先补一个 Demo 级方案:
POST /api/documents/{id}/rebuild-index POST /api/knowledge-bases/{id}/rebuild-index但语义要说清楚:
**retry:**失败后重试。
**rebuild:**重新生成索引。
Demo 级 rebuild 可以先做:
严格删除旧 MySQL chunk 严格删除旧 Milvus vector 严格删除旧 ES doc 重新 parse / chunk / embed / index注意:这里的关键是“严格删除”。
如果 Milvus 或 ES 删除失败,就不要继续重建。否则新旧索引会混在一起。
但这个方案只是过渡版,它有空窗期,也没有回滚能力。
真正生产级还是要走:
document_revision index_version active_index_version 异步 index_job 蓝绿切换 对账任务 旧版本 GC四、一句话回答那个评论
如果有人问:
新建文档或者修改树结构后如何重建索引,如何保证向量库同步?
我会这样回答:
Demo 版可以先做rebuild-index:严格清理旧 chunk、旧向量、旧关键词索引,再重新构建。
但生产级不能靠删除旧索引来保证同步。生产级应该做版本化索引:文档有document_revision,索引有index_version,知识库有active_index_version。
新索引后台构建,Milvus / ES / MySQL chunk 全部带 index_version。查询只读 active_index_version。新版本对账通过后,再原子切换 active_index_version。
同步不是靠 MySQL、Milvus、ES 的分布式事务,而是靠 MySQL 真相源、幂等索引任务、版本隔离和对账修复。
结论
RAG 工程化里,索引不是一次性产物,而是有生命周期的资产。
上传文档只是开始。真正要处理的是:
文档变了怎么办?
chunk 策略变了怎么办?
embedding 模型变了怎么办?
索引构建失败一半怎么办?
向量库和业务库不一致怎么办?
用户会不会查到半成品索引?
我的结论很明确:
小系统可以先做严格清理 + rebuild。生产系统必须做版本化索引 + 蓝绿切换 + 对账任务。
最后一句话:
向量库不是数据库,向量库是索引。
索引可以重建,但必须有版本。
同步可以最终一致,但必须能对账。
重建可以异步,但半成品不能被查到。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~