一句话定义
向量库的写删语义建立在不可变段 + tombstone(墓碑)之上:删除是打标记不是真删,更新是"写新标旧",新数据先在无索引的增量段暴力扫描兜底,直到 flush 与段合并(compaction)才"真正落定"——理解这套机制,才能解释所有"刚写入查不到/删了还在/越写越慢"现象。
为什么重要
算法库(FAISS)与数据库(Milvus/Qdrant)的本质差距就在这一篇:向量索引(尤其图索引)不支持高效原地修改,数据库用段式 LSM 风格的变通换来了 CRUD 能力,代价是可见性延迟、删除膨胀与合并开销。生产环境一半以上的"灵异现象"(kp-030 排错清单的常客)根因在此。
前置知识
kp-018(系统谱系)、kp-008(HNSW 图为何难删除——节点被邻居边引用)。
核心概念
- segment(段):一批向量的物理组织单位;分 growing(增量,无索引)与 sealed(封存,建索引)。
- tombstone(墓碑):删除标记的位图/列表;查询时过滤,物理回收延后到合并。
- upsert:按主键"存在则更新"——实现为新版本插入 + 旧版本打墓碑。
- 可见性(visibility):从写入成功到可被查询检索到的时间差;由 flush 周期与一致性级别共同决定。
- compaction(段合并):把多个小段/含墓碑段重写为干净段,物理清除删除与旧版本,恢复索引效率。
原理与机制
一次删除的完整旅程:客户端 delete → 记录主键进墓碑集合(内存+日志)→ 查询路径在扫段时跳过墓碑点(逻辑删除即刻生效,前提是一致性级别允许)→ 段内墓碑比例升高,图索引中该节点成"孤儿"(邻居边仍指向它,遍历时跳过)→ compaction 触发时重写段、真正移除向量并修复邻接 → 空间与性能回收。
一次更新的旅程:upsert(v') → 新向量进 growing 段 + 旧版本打墓碑 → 查询时新旧版本都可能被扫到,靠主键版本去重 → 合并时只留新版。存储瞬时膨胀:更新期间新旧两份并存。
一次写入的可见链:write ack(进日志/内存段)→ flush(落盘)→(sealed 后建索引才走快路径)→ 按一致性级别对查询可见。Strong 一致读等待最新日志位点;Eventually 读容忍滞后换吞吐。
设计后果清单:
- 高频小事务式更新是反模式——每条更新都产生"新版本+墓碑",段膨胀、查询去重开销涨;应攒批 upsert。
- 批量导入有专属快路径——先关索引/减副本灌数,后统一建索引,可比逐条插入快数倍到数十倍。
- 删除后立即 compaction 不一定划算——合并本身是重写 IO,通常按墓碑比例/段大小策略触发,而不是每删必合。
公式或模型
删除膨胀的直觉账:
有效数据率 ≈ 存活向量 / (存活向量 + 墓碑数 + 旧版本数)
例: 1 亿向量删改 2000 万次未合并 → 有效率 ≈ 83%, 内存与扫描同比浪费图示
t0: [seg1: v1 v2 v3 v4] ← sealed, 有索引
t1: DELETE v2 → 墓碑{v2}; UPSERT v3→v3'
t2: 查询: seg1(跳过v2, v3旧版去重) + [seg2 growing: v3' 新版暴力扫]
t3: compaction: seg1+seg2 → seg3 [v1 v3' v4] ← 物理清除墓碑/旧版实例或案例
电商知识库日均更新 5% 条目:初版逐条 upsert,两周后查询 p99 翻倍——根因是墓碑与小段堆积、growing 段占比过高(大量查询落在暴力扫描路径)。修复:批量定时 upsert + 把合并阈值调激进 + 查询避开合并窗口,p99 恢复。教训:写删节奏是容量规划的一部分(kp-026/029),不是"免费的 CRUD"。
直观类比
段式存储像"账本换页":旧账页不涂改(不可变段),错账画线标记(墓碑),更正写在最新的活页(growing 段);每月结账时(compaction)把有效条目誊进新账本、旧账归档。誊写期间新旧账都要查,所以查询要会"去重"。
常见误区
- "删了就查不到,改了立即生效"——逻辑删除/更新经过墓碑与去重后确实"结果正确",但依赖一致性级别与实现细节;跨副本传播、合并延迟都可能造成短暂旧值可见,强一致需求必须显式选级别并实测。
- "HNSW 支持删除所以随便删"——图索引删除靠墓碑+遍历跳过,墓碑堆积会让图有效连通性下降、召回与延迟劣化;大批量删除后主动触发合并。
- "向量库支持 ACID 事务"——多数向量库只保证单条/单批的原子性与最终一致,跨集合事务、外键约束等关系库语义不存在;需要事务就留在 pgvector(kp-020)或应用层补偿。
自测题
- 为什么删除用墓碑而不是立即从图索引摘除节点?
- upsert 期间存储为什么临时翻倍?
- 批量导入 1 亿向量的推荐姿势?
答:图节点被邻居出边引用,原地摘除要修改所有入边(随机写风暴);墓碑把删除变 O(1) 标记,物理回收推迟到批量重写(合并)时一次完成。
答:新版本完整写入新位置,旧版本在合并前不可物理删除——更新语义在此实现下是"插入+墓碑"的复合。
答:临时关闭/延后索引构建与降副本,按段批量写入落盘,再统一建索引并恢复副本——比边插边建索引快数倍,且产出更规整的段。
与其他知识点的关系
kp-017 陷阱 5(删除可见性)是本篇机制的现象面;kp-019 展示这套语义在 Milvus 的具体实现;kp-030 排错清单中"越写越慢/删了还在"条目全部回溯到这里。
延伸阅读
Wang et al., "Milvus: A Purpose-Built Vector Data Management System"(SIGMOD 2021)——段、日志与一致性级别设计的系统级论述,本篇机制的原始蓝本。