本节摘要:Qdrant 是一款用 Rust 语言编写的向量数据库,核心技术栈由 Rust、HNSW 近似最近邻索引、gRPC 与 REST 接口共同构成。它的诞生源于一个明确痛点:早期向量搜索要么靠内存里的近似近邻库,难持久化;要么塞进传统数据库,查询太慢。读完本节,你能讲清 Qdrant 的发展阶段,并理解 Rust、HNSW、gRPC 各自解决了什么具体问题,从而明白 Qdrant 的"快"和"稳"从哪来。这些技术选择环环相扣,共同撑起一个生产可用的向量数据库系统。
阅读完本节,你应当能够:
要理解 Qdrant 的技术栈,先得回到它出生的那个时间点,看当时做向量搜索的人有多难受。
深度学习普及之后,向量嵌入成了标配。模型把文字、图片吐成一串数字,开发者很快就遇到了一个现实问题:这些向量存哪、怎么查?当时主要有两条路,但都不顺。
一条路是用近似近邻库,比如在内存里维护图索引。它的优点是快,缺点是数据跟着进程走,一重启就没了,也没法和别的东西共享,谈不上生产环境。另一条路是把向量塞进传统数据库,当普通字段存。这条路的优点是能持久化、能管理,缺点是传统数据库的索引根本不会查高维向量,一查就退化成全表扫描,慢得没法用。
所以那个阶段的真实写照是:要快就没法持久化,要持久化就快不起来。这个矛盾,正是 Qdrant 这类专用向量数据库要解的题。
其实在 Qdrant 之前,学术界和工业界也试过别的手段。有人用局部敏感哈希把相近的向量哈希到同一个桶里,有人用基于树的结构去切分空间。这些方法在高维空间里都暴露出同一个毛病:维度一上去,索引效果就大打折扣,要么召回差,要么内存爆炸。于是大家慢慢形成一个共识——得为向量单独设计存储和索引,而不是在传统数据库上打补丁。
Qdrant 的答案很直接:做一个专门为向量设计的数据库。既然通用数据库的索引不认向量,那就自己写索引;既然内存库不持久,那就自己加落盘和恢复;既然要嵌进真实业务,那就把接口做齐全。理解了这三个"自己写",就理解了它整个技术栈的由来。
先看发展脉络。Qdrant 的演进大致能分成四个阶段,每个阶段都在补一块短板。
萌芽期,团队确认了一个判断:向量嵌入会成为数据检索的新常态,而市面上缺一个既快又稳、还好接入的专用库。核心构建期,他们做了两个关键决定——用 Rust 写核心,用 HNSW 做索引,这两个决定基本锁定了 Qdrant 的性能上限。功能拓展期,开始往"能用"里加"好用",载荷过滤、多种距离度量、数据持久化陆续补齐。企业级成熟期,则瞄准分布式、高可用、多语言客户端和托管云服务,让它从个人玩具变成团队能依赖的基础设施。
接下来拆技术栈。它由三块拼成:语言层、索引层、接口层。
先说 Rust。选它不是因为流行,而是因为数据库这种系统对两件事特别苛刻:性能和内存安全。Rust 编译成原生机器码,没有垃圾回收的停顿,查询延迟可控;同时它的所有权机制和借用检查器在编译期就把空指针、数据竞争这类内存错误拦住了,运行时少了很多崩溃的可能。对一台要长时间跑、扛大量并发请求的服务来说,这两点比"开发速度快"重要得多。
还有一层常被忽略的收益:Rust 的异步编程模型让 Qdrant 能并行处理大量请求。数据库大部分时间在等 I/O,异步模型让这些等待彼此重叠,同一个进程能服务的并发量就上去了。这种"稳、快、又能扛并发"的组合,是很多带垃圾回收的脚本语言很难同时满足的。
再说 HNSW。全称是分层可导航小世界图,听着唬人,原理可以用一张地图来理解。把向量想象成地图上的点,HNSW 造出好几层路网:顶层路稀,但每步跨得远,用来快速逼近目标区域;越往下路越密,用来在局部仔细找。查询时从顶层往下跳,像"先飞到大区、再步行到门牌号",省去了逐个比较所有点的力气。它牺牲的是一点点精度——不保证找到绝对最近的那个,但保证大概率找到很接近的。这正是一个工程权衡:用可控的精度损失,换来数量级的速度提升。这套分层结构其实借了跳跃表的思想——先在最稀疏的层大步跳,找到大致位置再逐层下沉,在密集层里精修。把"全局逐个比较"换成"分层逼近",就是 HNSW 快的根本原因。
最后说接口层。Qdrant 对外提供 gRPC 和 REST 两套接口。gRPC 走二进制序列化,性能好、支持流式,适合高频的内部调用;REST 则更通用,随便什么语言、什么工具都能直接发 HTTP 请求,适合调试和简单集成。两者之上再封装多语言客户端,让开发者用 Python、Go、Java 这些熟悉的方式操作。
除了这三层,向量之间的"距离"怎么算,也是技术栈的一部分。Qdrant 支持余弦、欧氏、点积等多种度量,选择哪一种,直接决定"相似"的定义。这个选择和嵌入模型绑定,建集合时定下,后面难改。它虽不像 Rust、HNSW 那样抢眼,却是决定检索质量的一颗关键螺丝,选错了,整个检索的质量都会往下掉。
下表把三层技术栈和它解决的问题对应起来,一眼就能看清"为什么选它"。
| 层次 | 技术选型 | 解决的痛点 |
|---|---|---|
| 语言层 | Rust | 无 GC 停顿、内存安全、高并发 |
| 索引层 | HNSW | 海量向量里毫秒级找近似近邻 |
| 接口层 | gRPC 加 REST | 高性能调用与通用接入兼顾 |
除了这三层,还有一块容易被忽视的底座:持久化与恢复。Qdrant 用预写日志记录每一次写操作,数据先落日志再应用,哪怕进程中途崩溃,也能靠重放日志把数据恢复到崩溃前的状态。正是这层保障,让"向量数据库"从"内存里的玩具"变成了"可以托付生产数据"的存储系统。它回答了那个最初的两难——要快,也能持久化,两者不再冲突。
技术栈不是越新越好,选型背后全是取舍。Rust 换来了性能和稳定,代价是团队招聘和上手成本比用更大众的语言高一些。如果你只是拿 Qdrant 当个黑盒用,这个代价由 Qdrant 团队背,与你无关;但如果团队要深度定制,就得掂量掂量有没有 Rust 能力。
HNSW 的参数也值得说一句。它有几个旋钮,比如每层的连接数和搜索时的候选数,调大一点召回率会高,但内存和查询耗时也会涨。默认值在大多数场景够用,别一上来就乱拧。
等数据量上到单机扛不住,就要动用分布式能力:把集合切成多个分片,每个分片再留副本。分片解决"存不下、查不动",副本解决"坏一个节点还能继续服务"。这套机制是 Qdrant 走向企业级的关键一跳,也是它区别于"单机索引库"的硬指标。换句话说,单机和分布式不是两种割裂的产品,而是同一个系统在不同规模下的两种姿态——从单机起步,数据涨了再平滑加节点。
| 取舍项 | 偏向一边 | 偏向另一边 |
|---|---|---|
| 连接数 | 大:召回率高、内存占用大 | 小:省内存、可能漏 |
| 候选数 | 大:更准、更慢 | 小:更快、略糙 |
| 接口选择 | gRPC:内部高频、低延迟 | REST:通用、易调试 |
⚠️ 常见坑:把 HNSW 当成"免费午餐",以为近似搜索只是慢一点。实际上它是一次权衡——精度换速度。对召回率要求极高的场景,得用更大的参数去补,而不能默认近似就等于精确。
💡 关键直觉:技术栈的每一层都对应一个具体的痛点在解。Rust 解"稳不稳",HNSW 解"快不快",gRPC 解"接得顺不顺"。看懂这个对应关系,比背参数有用得多。
Qdrant 是从什么时候开始的? 项目在 2021 年前后启动,早期先把 Rust 和 HNSW 这两个地基定下来,之后逐步补齐过滤、持久化、分布式和云服务。时间点是大概范围,重点看它的能力是怎么一层层长出来的。
Rust 会不会让 Qdrant 很难用? 对使用方不会。Rust 是服务端实现语言,你通过客户端和接口跟它打交道,用不到 Rust。上手难度的代价由项目自身承担。
gRPC 和 REST 只能二选一吗? 不是,Qdrant 两套都提供。内部高频调用走 gRPC,临时调试走 REST,可以混用。
HNSW 的近似搜索会漏掉正确结果吗? 有可能,但概率可控。它是用极小的精度损失换数量级的速度提升,大多数推荐、搜索场景完全够用。对召回要求极高的场景,可以把搜索参数调大。
学这个技术栈对我有什么用? 即便不深挖源码,知道 Rust 管稳、HNSW 管快、接口管接,也能帮你在遇到性能或召回问题时,判断该往哪一层排查。
单机和分布式该怎么选? 数据量小、访问量低,单机加持久化就够;数据量和并发上来、或对可用性有要求,再上分片加副本。别为没到来的规模提前买单。
向量维度对性能影响大吗? 影响明显。维度越高,存储和计算开销越大,HNSW 的索引也更占内存。所以维度不是越大越好,跟着嵌入模型走。
技术栈讲清了"凭什么快",下一节我们聚焦它到底"好在哪"——高性能、载荷过滤、易用接口这三样核心特性,各自对应什么业务诉求,又有什么代价。