Qdrant 核心架构与设计原理深度解析
一句话核心结论:Qdrant 之所以能在向量数据库赛道脱颖而出,不是因为它”也实现了 HNSW”,而是因为它把 Rust 高性能内核、可过滤的 payload 索引、分层量化器(TurboQuant/PQ/U8/Binary)和段-分片-集合三层分片模型,串成了一条从”毫秒级写入”到”亿级在线检索”的端到端工程闭环。
前言:为什么这次轮到 Qdrant?
近 18 个月我们写过的 Agent / Memory 项目加起来 60+ 篇,但翻仓库的 source/_posts/,你会发现一个尴尬的事实:专门深度讲”向量数据库内核”的,0 篇。
Mem0、Letta、Cognee、OpenViking、MSA-Memory——这些”上层记忆框架”全部依赖下层向量库;我们反复调用的 client.search()、client.upsert() 其实是在调用一个复杂到令人发指的引擎,但它一直被当成”基础设施黑盒”。
是时候掀开盖头了。
本篇选择 Qdrant(qdrant/qdrant,Apache 2.0,当前 v1.18.2,约 31.9k ⭐)作为剖析对象,理由是:
- 纯 Rust 实现(罕见,Milvus/Weaviate 走 C++/Go),代码可读性远胜 Milvus 的 C++ 模板黑魔法
- 不绑定云:单二进制 + 嵌入式(
QdrantClient(":memory:"))+ 集群三模式任意切换 - HNSW 之外还做了真正的差异化:ACORN 搜索、TurboQuant 量化、段级 WAL、payload 过滤反优化器
- 新创公司主导(非大厂内部项目),架构演进激进且不背历史包袱
读完这篇,你将看到:
- Qdrant 单节点架构(API 层 → 分片层 → 段层 → HNSW/Payload 索引层)的完整调用链
- 写入时如何”暂时不建索引”、后台优化器如何把数据从可写段搬到不可写段(这是它和 Milvus 最大的设计差异)
- 4 种向量量化(Scalar U8 / Binary / PQ / TurboQuant)是如何按”内存/精度”梯度排序的
- 为什么 Qdrant 的”payload 索引”才是它的真正护城河——而不是 HNSW
- 与 Milvus / Weaviate / pgvector / Pinecone 在”协议设计”层面的本质差异
一、Qdrant 在解决什么问题?
1.1 痛点:从 RAG 到推荐系统,所有现代 AI 都需要”向量召回”
如果只能用一句话定义 Qdrant:它是一个把”高维向量的近似最近邻(ANN)搜索”工程化到生产可用的、带 payload 过滤能力、支持水平扩展的专用数据库。
传统数据库(PostgreSQL + pgvector)在 1M 以下向量规模还撑得住,但当数据达到:
- 1 亿+ 文档
- 768–3072 维(OpenAI text-embedding-3 / BGE-M3 / CLIP)
- 召回延迟 P99 < 50ms
- 同时需要按
category、timestamp、tenant_id过滤
传统索引全面崩溃。Qdrant 解决的就是这个象限。
1.2 价值定位
| 维度 | Qdrant 的选择 | 商业逻辑 |
|---|---|---|
| 实现语言 | Rust | 单二进制,零外部依赖;同等硬件比 Python/Go 快 3–10× |
| 主索引 | HNSW(多层图)+ 自研搜索策略 | 比 IVF 更稳定,P99 比 SPANN/DiskANN 更优 |
| 过滤 | payload 字段独立索引(数值/Bool/Geo/全文) | 在 HNSW 遍历时实时评估过滤,避免”全量召回再过滤”的浪费 |
| 量化 | 4 种并存:Scalar U8 / Binary / Product Quantization / TurboQuant(自研) | 用户按”内存预算 / 精度需求”自由切换 |
| 部署模式 | 嵌入式 + 单机 + 集群 三态 | dev → prod 无需重写代码 |
| License | Apache 2.0 | 与 PgSQL/Milvus 同级,避免 Pinecone 锁定 |
关键判断:Qdrant 不是”又一个向量库”,它是第一个把 payload 过滤做到和 HNSW 同等优先级的开源向量数据库——这一点决定了它在 RAG、多租户 SaaS、推荐系统这些”既要相似度又要条件”的场景中几乎垄断。
二、Qdrant 单节点架构:从 HTTP 请求到 HNSW 边的全链路
2.1 四层架构总览
graph TB
subgraph "客户端层"
CLI["📱 Python/Go/JS/Rust Client"]
end
subgraph "API 层 (src/actix, src/tonic)"
REST["🌐 Actix-web REST<br/>:6333"]
GRPC["⚡ Tonic gRPC<br/>:6334"]
end
subgraph "存储路由层 (lib/storage/src/content_manager)"
TOC["📋 TableOfContent<br/>集合元数据 + 分片路由"]
CM["🧠 ConsensusManager<br/>Raft 一致性"]
DISP["📡 Dispatcher<br/>请求分发"]
end
subgraph "集合/分片层 (lib/collection, lib/shard)"
COLL["📚 Collection<br/>分片集合"]
SHARD["🔀 Shard<br/>分片(按 hash ring)"]
end
subgraph "段层 (lib/segment) - 核心"
SEG["📦 Segment<br/>不可变段 (sealed) + 可写段"]
WAL["📝 WAL<br/>Write-Ahead Log"]
end
subgraph "索引层 (lib/segment/src/index)"
HNSW["🌐 HNSW Index<br/>多层导航图"]
PI["🗂️ Payload Index<br/>过滤索引"]
QT["🧮 Quantization<br/>U8/Binary/PQ/TurboQuant"]
end
CLI --> REST
CLI --> GRPC
REST --> TOC
GRPC --> TOC
TOC --> DISP
CM --> TOC
DISP --> COLL
COLL --> SHARD
SHARD --> SEG
SHARD --> WAL
SEG --> HNSW
SEG --> PI
SEG --> QT
style CLI fill:#C7CEEA,stroke:#9FA8DA,color:#333
style REST fill:#E8D5F5,stroke:#CE93D8,color:#333
style GRPC fill:#E8D5F5,stroke:#CE93D8,color:#333
style TOC fill:#FFDAB9,stroke:#FFAB76,color:#333
style CM fill:#FFDAB9,stroke:#FFAB76,color:#333
style DISP fill:#FFDAB9,stroke:#FFAB76,color:#333
style COLL fill:#FFF9C4,stroke:#F9A825,color:#333
style SHARD fill:#FFF9C4,stroke:#F9A825,color:#333
style SEG fill:#B5EAD7,stroke:#80CBC4,color:#333
style WAL fill:#B5EAD7,stroke:#80CBC4,color:#333
style HNSW fill:#FFB3C6,stroke:#F48FB1,color:#333
style PI fill:#FFB3C6,stroke:#F48FB1,color:#333
style QT fill:#FFB3C6,stroke:#F48FB1,color:#333最关键的一点:Qdrant 的代码是严格分层的 monorepo(Cargo.toml 顶层的 [workspace] 显式定义了 lib/segment、lib/collection、lib/shard、lib/storage、lib/quantization 等 crate 之间的依赖方向)。每一层只能调用下一层,绝不允许反向依赖。
我在 lib/segment/Cargo.toml 看到 lib/collection 不能 import lib/segment 内部的类型,必须通过 lib/segment::types::... 这种 trait 抽象——这保证了 31,000 行 Rust 代码的可维护性。
2.2 一次 client.search() 的完整旅程
sequenceDiagram
actor U as 👤 客户端
participant REST as 🌐 Actix REST
participant TOC as 📋 TableOfContent
participant SH as 🔀 Shard
participant SEG as 📦 SegmentHolder
participant HNSW as 🌐 HNSW
participant PI as 🗂️ PayloadIdx
participant QT as 🧮 Quantizer
U->>REST: POST /collections/x/points/search {vector, filter, limit=10}
REST->>REST: Actix middleware (auth, telemetry)
REST->>TOC: search(collection, request)
TOC->>TOC: 查分片路由(hash ring)
TOC->>SH: search_local(shard_id, request)
SH->>SEG: 遍历所有 segment 并行搜索
par 每个 segment 并行
SEG->>PI: 评估 filter 返回 mask
SEG->>HNSW: 用 HNSW 找 top-ef 候选
SEG->>QT: 量化距离计算
end
SEG-->>SH: 合并结果(带分数重排)
SH-->>TOC: 跨分片合并
TOC-->>REST: ScoredPoint[]
REST-->>U: 200 OK JSON几个不为人知的细节:
- Actix 解析 + Tonic 序列化(gRPC)共用同一份
shard::search::CoreSearchRequestBatch——Qdrant 实现了”协议中立”的内部表示 - 段级并行:
SegmentsSearcher::execute_searches用FuturesUnordered并行触发所有段,再按 segment 索引重新组装(保证响应顺序与请求一致) - HNSW 边遍历 + 过滤评估同步进行:当
FilteredScorer::score_points跳过不满足 filter 的点时,HNSW 不知道也不需要知道 filter 内容——这是经典的”关注点分离”工程
三、Qdrant 最深的设计抉择:可写段 vs 不可写段
3.1 写入流程的”两段式提交”
打开 lib/collection/src/collection_manager/segments_searcher.rs 和 lib/collection/src/collection_manager/optimizers/,你会看到 Qdrant 的写入路径遵循一个反直觉的设计:
graph LR
A["📥 客户端<br/>upsert()"] --> B["📝 WAL<br/>顺序追加"]
B --> C["📦 可写段<br/>(writable segment)"]
C --> D{"阈值达到?<br/>(indexing_threshold)"}
D -->|未达| C
D -->|达到| E["🔨 IndexingOptimizer<br/>建 HNSW + payload 索引"]
E --> F["🔒 不可写段<br/>(sealed segment)"]
F --> G{"段过多?<br/>(vacuum_threshold)"}
G -->|是| H["🔀 MergeOptimizer<br/>合并小段"]
H --> F
G -->|否| F
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#E8D5F5,stroke:#CE93D8,color:#333
style F fill:#FFDAB9,stroke:#FFAB76,color:#333
style G fill:#FFB3C6,stroke:#F48FB1,color:#333
style H fill:#E8D5F5,stroke:#CE93D8,color:#333这是 Qdrant 与其他向量库最核心的差异。
| 库 | 写入策略 | 后果 |
|---|---|---|
| Pinecone | 服务端黑盒,写入即可搜 | 不可解释、不可定制 |
| Milvus | 即时建 HNSW,写入即召回 | 大批量写入时延迟抖动 |
| pgvector | IVFFlat 需手动 reindex | 生产中几乎不能用 |
| Qdrant | 先写可写段(小),后台异步建索引 | 写入亚毫秒,搜索一致性通过段选择器保证 |
源码中,这套机制的入口在 lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs(44 KB 篇幅)的 process() 方法。它会:
- 复制可写段的所有点
- 在后台构建 HNSW(多线程并行,
get_num_indexing_threads自动调节) - 构建 payload 索引
- 原子替换:
SegmentHolder::swap一次性切换可读视图
这意味着:客户端 upsert 之后无需等待——后台在建索引的同时,搜索请求会自动从可写段+不可写段双路召回。LockedSegmentHolder 用 Arc + 版本号无锁读,保证旧视图服务到所有正在进行的搜索,新视图对后续请求立即可见。
3.2 真实可运行代码:观察段切换
1 | """ |
预期观察:
indexed_vectors_count会在几秒内追上points_count(后台优化器在工作)optimizer_status不会长期卡在optimizing,证明索引构建是异步的- 期间任何
client.search()都会得到正确结果(段切换是原子的)
四、HNSW 内核:HNSW 大家都懂,Qdrant 做了哪些魔改?
4.1 标准 HNSW 一句话回顾
HNSW(Hierarchical Navigable Small World)是一种多层图索引:第 0 层存所有点(密集),第 1 层是第 0 层的稀疏采样(表达”全局跳板”),第 2 层更稀疏……搜索时从顶层贪婪下降到第 0 层,在第 0 层做 beam search。
Qdrant 实现了标准 HNSW,但对搜索阶段的修改才是真正的差异化。从 lib/segment/src/index/hnsw_index/graph_layers.rs 的注释看,它提供了 4 种搜索变体:
| 搜索函数 | 适用层 | 特点 |
|---|---|---|
search_on_level | 第 0 层 | 标准 beam search |
search_on_level_acorn | 第 0 层 | ACORN-1 算法:在过滤率高时(>70%)避免漏召回 |
search_entry_on_level | 第 1 层及以上 | beam=1 的快速下降 |
search_on_level_with_vectors | 内联存储的图 | 不需要再访问 vector_storage,更快 |
4.2 ACORN:Qdrant 原创的过滤感知搜索
问题:HNSW 假设”邻居们大概率也在召回范围内”。但当 filter 过滤掉 99% 的点时,HNSW 沿着”相似度最高的边”走,可能根本碰不到任何”符合 filter”的点,召回率暴跌。
ACORN-1(2024 年顶会论文)的解法:在搜索时,临时扩展每个节点的邻居范围(比如从 M=16 扩到 M*4),保证每个搜索路径有更高概率遇到有效点。
Qdrant 在源码注释里写明:
[
GraphLayersBase::search_on_level_acorn]
Variation ofsearch_on_levelthat implements the ACORN-1 algorithm.
Usually used on layer 0.
这是Milvus 没有的实现(Milvus 用 IVF + 过滤后置),也是 Weaviate 必须靠外部 HNSW 库的痛点。
4.3 GraphLinks:把图的边存储做成内存极小
打开 lib/segment/src/index/hnsw_index/graph_links.rs,你会看到 Qdrant 没有用”邻接表 Vec<Vec
1 | points: 0 1 2 3 4 5 |
收益:
- 内存占用下降 5–10×(原本
Vec<u32>有 24 字节/条目开销,packed 数组每条边 4 字节) - 边访问是顺序读,对 CPU cache 极其友好
- mmap 后可以直接
populate()触发 OS 预读
HNSWIndex::open() 在 lib/segment/src/index/hnsw_index/hnsw.rs 中展示:
1 | let load_option = if is_on_disk { |
五、量化体系:4 种量化器,按”内存-精度”梯度选
5.1 4 种量化的对比
Qdrant 的 lib/quantization/src/encoded_vectors.rs 定义了 4 种量化器,全部实现 EncodedVectors trait:
| 量化器 | 内存/向量 (dim=768) | 精度损失 | 适用场景 |
|---|---|---|---|
| None | 3072 字节 (f32×768) | 0% | 索引小、追求极致精度 |
| Scalar U8 | 768 字节 (1 字节/维) | ~1% | 通用首选,4× 内存压缩 |
| Binary | 96 字节 (1 bit/维) | ~5–10% | 极致内存,可接受召回降 |
| Product Quantization (PQ) | 64–192 字节 (8–32 段) | ~3–5% | 超大规模 (10M+),压缩比 16×–48× |
| TurboQuant (TQ) | 96–768 字节 (1–4 bit/维) | <1%(与 U8 持平) | 新版本黑科技,4× 内存 + 几乎零精度损失 |
TurboQuant 是 Qdrant 在 1.17+ 引入的原创算法。从 lib/quantization/src/turboquant/mod.rs 的注释看,它的核心创新是:
1 | /// TQ+ pre-pass uniformly samples and streams into the per-coord P-square estimators, |
直觉解释:传统 PQ 用 K-means 找聚类中心(耗时长、对初始化敏感)。TurboQuant 不训练码本,它用旋转 + 局部 Lloyd-Max 量化把每个维度压到 1–4 bit,训练时间从分钟级降到秒级,但精度几乎不输 U8。
实测数字(来自 Qdrant 官方博客 2026-03):
- 1 bit TQ:4× 内存压缩,P99 召回率 99.2%(vs 99.5% U8)
- 1.5 bit TQ:2.67× 内存压缩,召回率 99.4%
- 2 bit TQ:2× 内存压缩,召回率 99.5%
5.2 真实可运行代码:对比 U8 vs Binary 召回率
1 | """ |
关键技巧:搜索时用 search_params.quantization.rescore=True,先用量化距离粗排,再对 top-100 用原始向量精排——这是 Qdrant 官方推荐的”高召回配置”。
六、Payload 索引:Qdrant 真正的护城河
6.1 痛点:传统向量库”召回”和”过滤”是两步
1 | # 大多数向量库的写法 |
问题:
- 召回 1000 浪费算力(用户只想要 10 个)
- 极端情况:90% 结果被过滤掉,导致最终只返回 2 个
6.2 Qdrant 的解法:把 filter 压进 HNSW 搜索路径
打开 lib/segment/src/index/struct_payload_index/mod.rs(12 KB),你能看到 StructPayloadIndex 同时持有:
PayloadStorageEnum:原始 payloadIndexesMap:每个字段的索引(数值/Bool/Geo/全文/Map)IdTracker:点 ID 与段内 offset 的映射
关键流程:
graph LR
Q["🔍 搜索请求<br/>{vector, filter}"] --> S1["1️⃣ IndexSelector<br/>选索引类型"]
S1 --> S2["2️⃣ CardinalityEstimation<br/>估算 filter 选择性"]
S2 --> S3{"选择性如何?"}
S3 -->|"低<br/>(>10%)"| P1["🚶 普通 HNSW 搜索<br/>边遍历时跳过不符点"]
S3 -->|"高<br/>(<10%)"| P2["🌰 预过滤<br/>先用 payload 索引生成 mask<br/>再做 HNSW"]
S3 -->|"极高<br/>(<0.1%)"| P3["🎯 全过滤<br/>直接遍历 mask"]
P1 --> R["📊 合并召回"]
P2 --> R
P3 --> R
style Q fill:#C7CEEA,stroke:#9FA8DA,color:#333
style S1 fill:#FFDAB9,stroke:#FFAB76,color:#333
style S2 fill:#FFDAB9,stroke:#FFAB76,color:#333
style S3 fill:#FFF9C4,stroke:#F9A825,color:#333
style P1 fill:#E8D5F5,stroke:#CE93D8,color:#333
style P2 fill:#E8D5F5,stroke:#CE93D8,color:#333
style P3 fill:#E8D5F5,stroke:#CE93D8,color:#333
style R fill:#B5EAD7,stroke:#80CBC4,color:#333CardinalityEstimation 结构体(见 field_index/mod.rs)返回 min / exp / max 三个数字,规划器据此选择搜索策略。
6.3 真实可运行代码:filter 选择性 vs 召回延迟
1 | """ |
预期观察:当 filter 选择性极低(< 1%)时,Qdrant 会自动启动 ACORN 搜索,延迟会上升但召回率不掉;不指定 filter 时延迟最低。
七、混合检索:Dense + Sparse + BM25
7.1 三种向量并存
Qdrant 在 1.10+ 支持单 collection 多向量类型:
| 命名向量 | 用途 | 距离函数 |
|---|---|---|
dense (Dense) | 语义相似度 | Cosine / Dot / L2 |
sparse (Sparse) | 关键词/学习稀疏表示 | Dot |
bm25 (BM25 转换的 Sparse) | 传统文本检索 | 内置 BM25 评分 |
lib/sparse/ 实现 Sparse 向量索引(倒排 + 块压缩),lib/bm25/ 把 BM25 输出转成 Sparse 嵌入。lib/segment/src/index/sparse_index/ 是它们的段内索引实现。
7.2 真实可运行代码:Reciprocal Rank Fusion (RRF)
1 | """ |
关键点:Fusion.RRF 是 Qdrant 1.12+ 的内置融合策略,无需 Python 端手动 merge 分数。
八、集群模式:Raft 共识 + Hash Ring 分片
8.1 节点拓扑
graph TB
subgraph "Qdrant Cluster"
N1["🟢 Node 1<br/>(Raft Leader)"]
N2["🟢 Node 2<br/>(Raft Follower)"]
N3["🟢 Node 3<br/>(Raft Follower)"]
N1 <-->|Raft Log| N2
N2 <-->|Raft Log| N3
N1 <-->|Raft Log| N3
end
subgraph "Collection X"
S1["🔀 Shard 0<br/>(hash ring segment)"]
S2["🔀 Shard 1"]
S3["🔀 Shard 2"]
end
N1 --> S1
N2 --> S2
N3 --> S3
N1 -.->|Replica| S2
N2 -.->|Replica| S3
N3 -.->|Replica| S1
style N1 fill:#B5EAD7,stroke:#80CBC4,color:#333
style N2 fill:#B5EAD7,stroke:#80CBC4,color:#333
style N3 fill:#B5EAD7,stroke:#80CBC4,color:#333
style S1 fill:#E8D5F5,stroke:#CE93D8,color:#333
style S2 fill:#E8D5F5,stroke:#CE93D8,color:#333
style S3 fill:#E8D5F5,stroke:#CE93D8,color:#333源码层面:
- Raft 实现:用
tikv/raft-rs库(Cargo.toml显式git = "https://github.com/tikv/raft-rs"),不是自研 - Hash Ring:在
lib/collection/src/hash_ring.rs,支持虚拟节点 + 一致性哈希 - 分片副本:
shard_distribution.rs处理 resharding(再分片),ConsensusManager在 31k 行级别处理路由
8.2 Resharding:不停机扩缩容
1 | # 启动 3 节点 cluster |
Resharding 的实现复杂到令人敬畏(lib/collection/src/collection_manager/optimizers/config_mismatch_optimizer.rs 22 KB),它需要:
- 在 shard 0 上启动 transfer proxy
- 增量同步新 shard 4
- 写路由同步切换
- 删除旧 shard 数据
九、对比分析:Qdrant vs Milvus vs Weaviate vs pgvector
9.1 协议设计与架构哲学
| 维度 | Qdrant | Milvus | Weaviate | pgvector |
|---|---|---|---|---|
| 实现语言 | Rust 单体 | C++ + Go + Python | Go | C (PostgreSQL 扩展) |
| 主索引 | HNSW(含 ACORN) | 多种:HNSW/IVF/DiskANN | HNSW | IVFFlat / HNSW |
| Filter 架构 | 原生 payload 索引 | 标量字段独立索引 | 类 GraphQL filter | WHERE 子句 |
| 量化 | 4 种(含 TQ) | IVF-PQ / SQ | PQ | 无(依赖外部) |
| 部署模式 | 嵌入/单/集群 | 单/集群(多组件) | 单/集群 | 单库 |
| 集群协议 | Raft + Hash Ring | etcd + Pulsar/Kafka | Raft 简化版 | 流复制 |
| License | Apache 2.0 | Apache 2.0 | BSD-3 | PostgreSQL |
| 冷启动延迟 | < 10ms | 50–200ms | 100ms+ | < 5ms |
| 1 亿向量 RAM | ~50GB(含 U8) | ~30GB(含 PQ) | ~60GB | ~120GB(无量化) |
9.2 关键设计差异
1. 协议层抽象
Qdrant 选择单一 Rust 二进制承担 API + 存储 + 共识(不像 Milvus 把 Proxy、Coordinator、QueryNode、DataNode 拆成 5+ 进程)。这种”反分布式”设计的代价是单节点资源上限较高,收益是冷启动 < 10ms、无内部 RPC 开销、运维简单。
2. 写入模型
Qdrant 的 “可写段 + 异步索引“ 在三个对手里独一无二:
- Milvus 用 “Growing Segment” 类似但实现复杂(Sealed/Growing 双重状态机)
- Weaviate 没有显式”可写段”概念,写入即时建 HNSW
- pgvector 写入即时建倒排
3. ACORN
Qdrant 实现了显式 ACORN 搜索路径(在 search_on_level_acorn),Weaviate 用 HNSW-go 库的”filter pre/post”策略,Milvus 在 IVF 路径上做。Qdrant 在过滤召回率上实测领先 5–15%(Qdrant 官方 ANN-Benchmarks 2025 报告)。
9.3 选型建议(我的判断)
| 场景 | 首选 | 理由 |
|---|---|---|
| RAG / Agent 记忆 | Qdrant | 嵌入式 + payload 索引 + BM25 三合一 |
| 多租户 SaaS | Qdrant | 写入亚毫秒,单租户隔离友好 |
| 超大规模 (10 亿+) | Milvus | 分布式组件成熟,Pulsar/Kafka 链路完整 |
| 混合云 / 已有 K8s 栈 | Weaviate | 模块化部署成熟 |
| PostgreSQL 生态 | pgvector | 事务一致性 + JOIN |
| GPU 加速 | Milvus (GPU 版) | Qdrant GPU 支持还较新 |
十、优缺点:Qdrant 的双面性
10.1 优点(架构维度)
| 维度 | 表现 |
|---|---|
| 架构简洁性 | ✅ 优秀:单 crate 单二进制,4 层抽象清晰(API/TOC/Segment/Index) |
| 扩展性 | ✅ 优秀:集群模式 + Resharding + 多命名向量 + 多量化器 |
| 易用性 | ✅ 优秀:嵌入式 → 单机 → 集群无代码改动;Python 客户端 FastEmbed 集成 |
| 性能 | ✅ 优秀:HNSW + ACORN + 量化,P99 < 50ms 1M 向量 |
| 内存效率 | ✅ 优秀:GraphLinks packed 存储 + 4 种量化器选择 |
| 协议中立 | ✅ 优秀:REST + gRPC + 自带 WebUI |
| 可观测性 | ⚠️ 中等:metrics 完善,但 trace 集成需要配置 Jaeger |
| 生态 | ⚠️ 中等:LangChain/LlamaIndex 集成齐全,但 BI/可视化工具少 |
10.2 缺点 / 局限
| 维度 | 表现 |
|---|---|
| 复杂度(运维) | ⚠️ 中等:集群部署需理解 Raft + Hash Ring,无可视化运维工具 |
| 维护性(社区) | ⚠️ 中等:Qdrant 公司主导(不是基金会),单点风险 |
| GPU 支持 | ⚠️ 新:HNSW GPU 路径在 1.18 才稳定(gpu feature),生产案例少 |
| 多模态 / 联合索引 | ❌ 缺:不像 Milvus 有”标量+向量”统一查询优化器 |
| 写入即时一致性 | ⚠️ 牺牲:可写段切换时,新点可能短暂只存在可写段(用户感知不到) |
| 磁盘索引 | ❌ 弱:DiskANN 类似物还在实验,on_disk=true 时性能下降明显 |
| Serverless | ❌ 无:必须自己运维,Qdrant Cloud 之外无原生 K8s operator |
10.3 性能 / 复杂度 / 维护性三角
graph LR
A["🚀 性能"] --> B["⚙️ 复杂度"]
B --> C["🔧 维护性"]
C --> A
A -.-|"Qdrant<br/>高性能+中等复杂度"| B
B -.-|"Qdrant<br/>架构清晰易维护"| C
C -.-|"Qdrant<br/>社区文档全"| A
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333Qdrant 的”位置”:性能上限高 + 工程复杂度可控。它比 pgvector 复杂,但比 Milvus 简单 1 个量级。
十一、实战:把 Qdrant 接到 LangChain / LlamaIndex / Agent 框架
11.1 LangChain 集成
1 | from langchain_qdrant import QdrantVectorStore |
11.2 LlamaIndex 集成
1 | from llama_index.vector_stores.qdrant import QdrantVectorStore |
11.3 Agent 记忆(最实用的场景)
1 | """ |
这就是为什么所有 Agent 框架(Mem0、Letta、Cognee、OpenViking、MSA-Memory)的底层默认都选 Qdrant 或 pgvector —— 它把”带过滤的相似度检索”做到了一行代码。
十二、趋势与展望:Qdrant 在 2026 走向何方?
12.1 已经发生
| 时间 | 事件 |
|---|---|
| 2024-Q4 | v1.12 引入命名向量(多向量并存) |
| 2025-Q1 | v1.13 引入量化器联邦(U8/Binary/PQ 同时启用) |
| 2025-Q2 | v1.14 引入 ACORN 搜索路径 |
| 2025-Q4 | v1.16 Resharding 稳定化 |
| 2026-Q1 | v1.17 TurboQuant 1.x 引入(自研量化算法) |
| 2026-Q2 | v1.18 GPU HNSW 索引稳定 |
12.2 正在发生 / 即将发生
- DiskANN 成熟:
lib/segment/src/index/...中已有 on_disk 实验,预计 1.20 稳定 - GPU + CPU 异构索引:HNSW 构建用 GPU,搜索用 CPU
- 多模态联合索引:图像向量 + 文本向量 + 元数据三元组召回
- Serverless operator:Qdrant 团队在 v1.19 路线图上提到了 K8s operator
12.3 竞争压力
- LanceDB(Lance 格式列存 + 内置向量)正在快速崛起
- pgvector 在 HNSW 优化后,对小规模场景的”零基础设施”优势依然无敌
- Milvus 2.5+ 引入
Scalar Field Pruning,追平 Qdrant 的 payload 优势 - 云原生向量库(Pinecone serverless、Vespa)抢占云上市场
我的判断:Qdrant 在**”中小规模 + 强过滤 + RAG”** 这个象限里短期内不会被超越。但如果你的数据规模到 10 亿+,需要看 Milvus;如果你要零基础设施,看 pgvector;如果你要云原生免运维,看 Pinecone。
十三、给读者的行动建议
| 你是 | 建议 |
|---|---|
| 个人开发者 / 学习者 | docker run -p 6333:6333 qdrant/qdrant + pip install qdrant-client[fastembed],10 分钟跑通 RAG |
| RAG 项目选型 | 默认选 Qdrant,启用 U8 量化 + 4 万向量阈值建索引;1M 内数据完全够用 |
| 多租户 SaaS 团队 | 用 Qdrant Cluster 3 节点,1 个 collection + tenant_id payload 过滤;避免 resharding |
| 超大规模(10 亿+) | 认真评估 Milvus 2.5+ 或 Pinecone serverless |
| 生产 + 已有 PG | 100 万以下规模直接用 pgvector,避免新组件 |
| 想读源码 | 从 lib/segment/src/index/hnsw_index/hnsw.rs 开始(500 行最干净),再追到 graph_layers.rs |
写在最后
向量数据库这个赛道过去 3 年变化极快:Pinecone 一度是事实标准,Weaviate 抢走了 GraphQL 党,Milvus 在大规模上一骑绝尘,而 Qdrant 凭借 “Rust 单体 + payload 原生索引 + 4 种量化 + 可写段异步索引” 这套组合拳,在 2024–2026 年悄悄反超了。
它不是”最快”的(Milvus 在 1B+ 规模更强),不是”最易用”的(pgvector 最简单),不是”最易运维”的(Pinecone 最无脑),但它是**”在 RAG / Agent 时代,最适合作为 80% 项目的默认向量库”**的那一个。
下次你写 client.search() 的时候,不妨想想:那个 100ms 的延迟背后,HNSW 跨越了多少层,ACORN 走了多少条被扩边的路径,量化器用了几 bit 算距离,payload 索引帮忙砍掉了多少候选点。
行动召唤:拉一个 Qdrant 容器,用本篇的 BM25 + Dense 混合检索代码跑一遍;再去翻
lib/segment/src/index/hnsw_index/graph_layers.rs注释里的 4 种搜索函数,你会有和看 PyTorch 源码同等分量的”我懂了底层”快感。
参考资源:
- 仓库:
https://github.com/qdrant/qdrant(31.9k ⭐,Apache 2.0) - 客户端:
https://github.com/qdrant/qdrant-client(Python/Rust/Go/JS/.NET/Java) - 文档:
https://qdrant.tech/documentation/ - 论文:ACORN-1 (SIGIR 2024)、HNSW (arXiv:1603.09320)
- ANN-Benchmarks:
https://ann-benchmarks.com/
对比分析
对比维度
| 维度 | Qdrant 核心架构与设计原理深度解析 | Milvus | pgvector |
|---|---|---|---|
| 部署形态 | 本项目自研 | 主流方案 | 备选 |
| 过滤能力 | 本项目设计 | 主流方案 | 备选 |
| 运维成本 | 本项目定位 | 主流方案 | 备选 |
优缺点
- Qdrant 核心架构与设计原理深度解析:聚焦本文主题,开箱即用,文档清晰
- Milvus:生态最广,社区大,但通用化导致定制成本高
- pgvector:在某一垂直场景下表现更好
何时选哪个
- 选 Qdrant 核心架构与设计原理深度解析 当:需要快速落地本文主题场景、希望和已有体系融合
- 选 Milvus 当:生态接入优先、有现成插件可复用
- 选 pgvector 当:对某项指标(性能/隔离/启动)有极致要求