构建
- 取 N/M (M为HNSW图中的节点邻居数,最底层为2M)的点作为上层导航图先进行构建,再将所有 N 点利用上层导航图完成划分分片。
- 上层导航图先利用聚类算法根据聚集关系进行划分,再通过自搜索方式进行微调划分关系。最终的划分关系要求使得最终的底层分片划分符合负载均衡。
- 自搜索:以该点作为目标进行搜索(efsearch=efs)对得到的 efs 个结果进行投票,每个结果以自己所在的分区进行投票,将目标点移动到票数最多的分区上。
- 负载均衡:将全量数据点通过上层导航图划分到底层不同分片上时能确保不会出现热点分片,不要求做到绝对均衡,要求控制下限,不要出现差距悬殊的分片影响系统性能。同时该架构的设计不应影响图结构的空间局部性以及图的拓扑感知结构。
- 导航图分配点到分片上时,分片同步进行索引构建。对于多分配,则相当于多个分片都接收到了要构建的点,由于分配速度比构建速度快,需要做好缓冲,避免同步阻塞。
- 分配与多分配:一个点应该分配到哪个分片上,应当由其在导航图上的搜索结果确定,以该点作为目标点在导航图上以 efsearch=efs 进行搜索,对于得到的搜索结果,每个结果根据其所在的分片进行投票,最终将目标点分配到所有票数最高的分片上,若最高票为 1 票,则只分配到最近的分片上。
- 分片上构建点时构建完整的多层 HNSW 图,完成构建后只保留分片上 HNSW 图的最底层图。
搜索
- 搜索时根据导航图搜索结果所在的分片来决定哪些分片需要参与搜索,导航图搜索结果中一个分片上的结果点越多,该分片的 efsearch 越大,efsearch= a * EP+b,EP 是该分片的搜索结果个数。
- 在底层图中进行搜索时,以导航图中的结果点作为入口点直接在底层分片的最底层开始搜索。
“聚类初始化与自搜索投票微调”部分,默认 search_ef=100、投票结果数 k_overlap=10,不是定义中的“efs 个结果”,请改成搜 efs=10,构建阶段的搜索令 efs=topk 就可。
暂时移除负载均衡的设计,只要求导航图使用聚类进行初始分片划分以及使用自投票进行微调。暂且不考虑对底层分片的均衡性进行干预。
严格采用“一个点应该分配到哪个分片上,应当由其在导航图上的搜索结果确定,以该点作为目标点在导航图上以 efsearch=efs 进行搜索,对于得到的搜索结果,每个结果根据其所在的分片进行投票,最终将目标点分配到所有票数最高的分片上,若最高票为 1 票,则只分配到最近的分片上。”的多分配策略。
实现一边通过导航图进行分片数据分配,一边在分片上构建独立的完整 HNSW 图,建成图后不删除非底层图,而是在搜索时直接在最底层以入口点作为起始点开始搜索。
• 已按最终要求完成代码改进,未引入任何负载均衡或分配后调整。
主要改动:
- Orion auto-shard 对所有非空 segment 强制构建完整 HNSW,包括只有 1 个点的分片。lib/shard/src/optimizers/config.rs:184 lib/collection/src/optimizers_builder.rs:254
- 公开 indexing_threshold=0 仍表示禁用索引;内部强制索引语义仅对 Orion 生效,不影响普通 Qdrant 集合。lib/collection/src/config.rs:495
- 完整多层 HNSW 永久保留,同时维持一个空 appendable segment,方便后续插入。
- 显式导航入口存在时,只在 L0 执行 MultiEP 搜索。
- 当前 segment 不包含入口时直接返回空,不再回退默认 HNSW 入口或遍历非底层。lib/segment/src/index/hnsw_index/graph_layers.rs:659
- Plain segment 收到导航入口搜索时不再执行全量扫描,避免绕开 L0 HNSW 语义。lib/segment/src/index/plain_vector_index.rs:83
- 自定义分片构建工具增加完整性门槛:必须全部副本完成索引,并满足每分片“一个完整 HNSW + 一个 appendable segment”。tools/qdrant_two_level_routing_experiment.py:6192
验证:
- Python/工具测试:256 项通过。
- Rust 阈值语义测试:3 项通过。
- Rust L0 禁止回退测试:1 项通过。
- Rust Orion policy 测试:3 项通过。
- Rust 单点分片实际优化测试:通过,确认生成一个单点完整 HNSW 和一个空 appendable segment。
- git diff –check 通过。
源码已完成,但当前在线 Qdrant 进程尚未替换为新二进制或重启;部署后这些服务端行为才会生效。
边分配、边构建分片
尚未实现完整流水线。先算完并发布全部分配产物,再导入。已有的是导入阶段的分片并发。
我要求的是边计算分片分配,边在获得插入点的分片上进行索引的构建。因为从宏观上来看,这就是一个整体的 HNSW 的构建过程,所有分片都参与到构建过程中。如果人为分开导航图分配与底层图构建的过程,就会导致集群的 CPU 利用率低。
不同 Query 的导航与分片执行流水重叠
同一批请求中仍有屏障。先等待整批导航任务完成,再发起分片搜索;不同独立请求可以重叠,但批内尚未做到逐 Query 导航完成就下发。
你认为如何实现才能最大化搜索时的性能表现?我直观上觉得最细粒度地并行是性能最高的方式。任何屏障或者互相阻塞等待都会导致 CPU 空转进而导致 CPU 利用率降低。
构建完整图后只保留 L0
当前
agent.md:12明确要求保留完整多层图,代码也按这个方向执行。此前记录要求“只保留 L0”;如果旧要求仍代表你的 idea,这就是实质变化,会影响索引空间和维护成本。
过去的要求过时了,明确要求构建时底层分片保留完整的多层图,在搜索时从底层分片的 L0 入口点对应的点进行搜索不代表除了 L0 的其他层不再需要了。
“逐点自搜索后迁移”
当前是同步轮更新。如果你想的是按顺序立即更新 owner,两者可能产生不同最终分区。
按顺序立即更新会导致很严重的依赖问题,严重影响并发。我建议采用按轮进行更新,更新几轮,当达到上限或者变化量小的时候停止。
“按聚集关系聚类”
当前具体实现是向量上的 K-Means,距离计算为平方欧氏距离;不是基于导航图边的图划分。若你指的是图社区聚类,这里有概念差异。
此处的聚类就是在 HNSW 的图结构上再次按照空间距离进行聚类划分,跟图结构本身无关。在余弦距离数据集中由于搜索的时候比较的是余弦距离,而不是归一化后的欧氏距离,因此我认为在导航图聚类时应当采用与搜索时行为相同的距离计算的行为,根据此原则来选择聚类的方式。
所有查询都走 Orion 路由
当前只支持匹配导航向量的无过滤 dense nearest 查询等条件。带 filter、exact、其他向量类型等会走普通 Qdrant 路径;批内一个请求不符合条件,也会让整批退出 Orion 路由。
当前场景只面向普通的向量检索,不考虑特殊种类和场景下的向量检索。