Skip to content

4.3.4 新增、修改、删除数据分别怎么同步到索引? ​

先给结论:新增、修改、删除虽然都属于“数据变了”,但它们对索引的影响完全不同。把三类变化混成一种写入动作,通常就是旧数据残留、重复索引和脏结果的开始。

所以更稳的同步思路通常不是“统一重跑一下”,而是分别处理:

  • 新增:把新对象写进去
  • 修改:替换受影响对象
  • 删除:让旧对象彻底失效或物理删除

一、新增数据怎么同步 ​

新增是三类变化里最简单的一类。

一般流程是:

  1. 识别到新文档或新记录
  2. 做清洗和切块
  3. 生成稳定 ID 和 metadata
  4. 写入索引

新增最需要注意的不是“能不能写进去”,而是“写进去以后能不能和已有对象稳定共存”。

至少要保证:

  • doc_id 不冲突
  • chunk_id 生成规则稳定
  • metadata 字段完整
  • 不会把草稿、测试数据、未发布数据提前写进生产索引

二、修改数据怎么同步 ​

修改是最容易被低估的一类。

因为修改通常不是简单“覆盖一下文本”,而是要回答:

  • 哪些 chunk 受影响
  • 旧 chunk 怎么处理
  • 版本关系怎么维护

一个更稳的处理顺序通常是:

  1. 根据 doc_id 找到该文档已有索引对象
  2. 对最新内容重新清洗和切块
  3. 生成新对象集合
  4. 对比新旧对象
  5. 对变化对象做 upsert
  6. 对已经不存在的旧对象做 delete 或失效

这里最关键的是:修改不是只加新对象,还要处理旧对象退出。

如果只做 upsert,不处理旧对象,系统很快就会堆积重复内容和历史残留。

三、删除数据怎么同步 ​

删除在生产环境里经常比新增更重要。

因为删除通常意味着:

  • 内容已过期
  • 权限已失效
  • 资料不再可见
  • 文档被撤回

如果源数据已经删除,但索引对象还残留,用户就可能继续搜到不该出现的内容。

更稳的删除路径通常有两种:

1. 物理删除 ​

也就是直接把索引对象删掉。

适合:

  • 文档确认下线
  • 数据不再需要保留
  • 没有保留历史版本的要求

优点是结果最干净,缺点是回滚能力较弱。

2. 逻辑失效 ​

也就是保留对象,但把它标记成:

  • status = inactive
  • is_deleted = true
  • valid_to < now

然后在检索前过滤掉。

适合:

  • 需要审计或回查
  • 可能需要回滚
  • 删除动作要分阶段生效

优点是可恢复、可追踪,缺点是你必须确保过滤逻辑绝对稳定。

一个更实际的对照表 ​

你可以先把三类变化理解成下面这样:

变化类型主要动作最容易出的问题
新增新建索引对象ID 不稳定、未发布内容误入库
修改重新生成并替换受影响对象旧 chunk 残留、重复对象并存
删除物理删除或逻辑失效源数据没了,索引里还残留

这也是为什么不能把三类变化统一粗暴处理成“重新写一次”。

一个最小示意 ​

python
def sync_change(event):
    if event["op"] == "create":
        chunks = build_chunks(event["doc"])
        upsert_chunks(chunks)

    elif event["op"] == "update":
        old_chunk_ids = load_chunk_ids(event["doc_id"])
        new_chunks = build_chunks(event["doc"])
        new_chunk_ids = [chunk["chunk_id"] for chunk in new_chunks]

        upsert_chunks(new_chunks)
        delete_chunks([cid for cid in old_chunk_ids if cid not in new_chunk_ids])

    elif event["op"] == "delete":
        delete_chunks_by_doc_id(event["doc_id"])

这段代码只是示意,但至少体现了一个关键点:

不同类型的变化,索引侧动作应该不同。

实际落地时,最值得先做好的 4 件事 ​

如果你准备把这三类同步做稳,至少先确保:

  1. 每篇文档都有稳定 doc_id
  2. 每个索引对象都有稳定 chunk_id
  3. 每次同步都有可追踪的变更事件或时间戳
  4. 删除动作有明确策略,到底是物理删还是逻辑失效

这 4 件事没站稳,后面很容易出现:

  • 改了一次,结果多了一份
  • 删了源文档,检索里还在
  • 回答偶尔命中历史版本

一个常见误区 ​

很多人会把“修改”处理成:

  • 新内容直接 upsert,旧对象先不管

这短期看似乎能用,长期几乎一定会变脏。

因为切块一旦变化,旧对象就未必会被覆盖,结果就是:

  • 新块写进来了
  • 旧块也还在
  • 同一主题在索引里出现两到三套表述

这正是很多系统“越更新越乱”的根源。

一句话总结 ​

新增、修改、删除三类变化必须分开处理。新增要稳定写入,修改要替换受影响对象并清掉旧对象,删除要确保旧内容真正退出检索范围。把三类变化都当成同一种写入动作,索引很快就会变脏。