向量数据库:Chroma 和 Qdrant 怎么选
知识有了,得给它安个家
上一章我们把文档变成了一串串向量,但这些向量总不能散落在内存里随风飘。它们需要一个专门的仓库:存得下、找得快、还能按条件筛一筛。这个仓库就是向量数据库。你可能要问,MySQL 存 JSON 也行啊,干嘛非得新学一个?核心区别就一条:一般的数据库存完就完了,而向量数据库天然会按距离建索引,支撑相似度检索这一特殊查询。
打个比方,普通数据库像按书名编号排放的图书馆,你报书名它找得飞快,但你问有没有讲年假制度内容差不多的书,它只能把所有书翻一遍。向量数据库则是一开始就按照内容主题把书排布好了,你的问题一进来,它知道往哪个区域走。对于私有知识库这个场景,这不是优化项,是刚需。
市面上向量数据库这两年冒出来一票,老牌的 Milvus、云上的 Pinecone、Redis 加个模块也能凑合。但对本地小团队来说,真正进入候选名单的通常是两个:Chroma 和 Qdrant。一个主打轻量易用、五分钟跑通,一个主打生产级功能、过滤能力强。这篇就把两者掰开揉碎比一比,帮你少走弯路。
正式比较之前,先打个预防针:工具之争在技术社区里很容易升温,但请你记住,知识库项目最后拼的是检索效果,不是选型姿态。我见过太多团队在选型会议上一耗就是两周,文档一片还没灌进去。选个差不多的先跑起来,遇到真瓶颈再换,比反复纠结强。这篇给的是决策依据,不是宗教立场。
向量数据库到底多做了什么
要让外行也理解它的价值,得看看检索这一步在底层是什么。假设你的知识库有十万片文档向量,用户提问变成一个新向量,要找出距离最近的五个。暴力解法是遍历十万条逐个算余弦距离,在普通机器上大概要跑半秒,十万个用户同时提问就直接爆炸。
向量数据库的看家本领是近似最近邻索引(ANN),最常用的是 HNSW 算法。它把向量按空间位置预先构筑成一张多层图结构,查询时从顶层粗定位、逐层细化,几步就能锁定目标区域。结果是十万条数据里找近邻从半秒压到几毫秒,代价是结果可能不是严格的前五名,偶尔混进一两个实际排名靠后的。对知识检索来说这个精度损失可以接受,毕竟我们一般取 top 5 然后交给模型去读。
HNSW 的效果受两个参数影响:层数与邻居数。层数越多图越稀疏,跳得越快但占内存;每个节点的最大邻居数越大,召回越准但构建越慢。幸运的是两个库都给了合理默认值,不是特殊场景不用动。真正需要你关心的是另一个动作——量化。默认每个维度用 32 位浮点存,百万片 768 维向量要吃掉三个 GB;开 scalar 量化后每个维度降到 8 位,内存直接瘦到四分之一,召回率只掉一两个百分点。生产环境这几乎是个必开项。
除了索引结构,向量数据库还做了三件工程上的事。一是租户与集合管理,不同业务线的数据可以一个实例分而治之;二是元数据过滤,检索时同时按条件筛除,比如只查部门等于财务的文档;三是持久化与备份,断电不丢、可迁移。这些功能在关系型数据库里你得自己拼,在向量数据库里是开箱即用的。
| 能力 | 传统关系数据库 | 向量数据库 |
|---|---|---|
| 精确查找 | 强项,索引成熟 | 也支持 |
| 语义相似检索 | 不支持或需插件 | 核心能力 |
| 十万级向量检索速度 | 全表扫描秒级 | 毫秒级 |
| 元数据过滤后检索 | 先筛再算,较慢 | 内部联合优化 |
| 维护成本 | DBA 需专人 | 同等或更低 |
顺便澄清个误区:Chroma、Qdrant 这些名字听起来像完全不同的物种,其实对外接口大同小异,都是创建集合、插入向量、查询相似度这一套。学会一个,另一个半天就能上手。选型的重点不在语法,而在部署形态、性能曲线和过滤能力这些工程属性上。
再绕回灌库这件事本身,还有个小自由度值得提前想清楚:向量里到底存不存原文。两种做法都常见。只存向量,原文留在对象存储或关系库里,检索时按 ID 回查,库小查询快但链路多一跳;原文直接塞在元数据里,一次检索全都有,省事但占地方,文档多到百万级时存储成本会很难看。我个人的折中是原文存数据库、向量库只挂一个 ID 和少量过滤字段,检索完再去原来的库取全文——多一步换来的是清晰的职责和更低的长远成本。
Chroma:五分钟跑通的轻量选手
Chroma 的定位就是为开发者友好而生的。一行 pip 装本地版,一个 docker 跑服务版,API 简洁到没多少学习成本。你的知识库如果文档量在几十万片以内、并发不高、团队就两三个开发,它几乎是无脑选择。语言上官方重点支持 Python 客户端,JS 也有但生态稍弱。
我自己第一次搭知识库就是 Chroma 起手,从 pip 装完到完整跑通灌库加检索,一个下午的事。它最俘获人心的点在于开发体验与调试体验一致:本地嵌入式模式不需要起服务,直接在脚本里 import 就能用,单步调试时变量全是可见的 Python 对象。等代码定型了再切到 Docker 服务版,改的只有一行客户端初始化。这种顺滑度让你在原型阶段几乎没有心智负担。
本地嵌入版装起来是真的简单:
pip install chromadb
服务版用 Docker,这是上生产时的常见形态:
docker run -d --name chroma -p 8000:8000 -v ./chroma-data:/chroma/chroma chromadb/chroma
数据落在宿主机的 ./chroma-data 目录里,容器删了数据还在,这点在调试阶段很方便。写入的代码也少得可怜,Python 里几行就完成建库写向量:
import chromadb
client = chromadb.HttpClient(host="localhost", port=8000)
col = client.get_or_create_collection(name="hr_docs")
col.add(ids=["doc1"], documents=["服务满十二个月可享受带薪年休假五日"],
metadatas=[{"dept":"hr"}])
results = col.query(query_texts=["新人第一年有几天假"], n_results=5)
注意 Chroma 默认用自带的 embedding 模型算向量,开箱即用但效果中规中矩。要换成上一章在 Ollama 里跑的 nomic-embed-text,得在创建集合时传入自定义的 embedding 函数,官方文档里有明确示例。这个替换动作几乎是每个团队的必经之路,默认模型撑原型可以,撑产品不够。
Chroma 的短板同样明确:百万级向量以上性能会肉眼可见地掉,高级过滤只支持简单的相等与范围组合,没有 gRPC 接口,高并发下靠 HTTP 有些勉强。它就像一辆城市代步车,通勤灵活够用,上高速跑长途就有点抖。
还有一个隐性代价值得一说。Chroma 把 embedding 函数放在客户端实现,意味着灌库代码和检索代码必须用同一套 embedding 函数,换机器、换语言时容易踩坑。另外它的过滤是在检索后做的,先按相似度捞出结果再筛元数据,如果过滤条件很严格,捞上来的前二十条里可能没几条满足条件,效果上就是命中不了想看的文档。这个设计上限决定了它不适合过滤密集型场景,别抱侥幸心理。
Qdrant:为生产环境而生的选手
Qdrant 的画风完全不同,Rust 写的,性能压榨是主要卖点。它最亮眼的设计是过滤与向量检索的深度融合:你可以先按元数据条件筛一遍,再在结果里做相似度查找,整个过程在服务端一次完成,性能损失很小。企业知识库往往要按部门、密级、时效先过滤再检索,这个能力恰好打在痛点上。
部署同样一行 Docker:
docker run -d --name qdrant -p 6333:6333 -p 6334:6334 -v ./qdrant-data:/qdrant/storage qdrant/qdrant
6333 是 HTTP 接口,6334 是 gRPC 接口,后者吞吐高一个量级,批量写库时务必走 gRPC。以下示例是先按元数据过滤再相似度检索的典型用法:
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, Filter, FieldCondition, MatchValue
client = QdrantClient(host="localhost", port=6334)
client.recreate_collection("hr_docs", vectors_config=VectorParams(size=768, distance=Distance.COSINE))
client.upsert("hr_docs", points=[{"id":1, "vector":[0.1]*768,
"payload":{"text":"年休假五日","dept":"hr"}}])
res = client.search("hr_docs", query_vector=[0.1]*768, query_filter=Filter(
must=[FieldCondition(key="dept", match=MatchValue(value="hr"))]), limit=5)
看到区别了吗?过滤条件是查询的一部分而不是两步操作。另外它的 payload 设计天然支持复杂嵌套,一个点上可以挂任意 JSON 元数据,这部分在 ol-0304 讲切片字段设计时会用到,那里会把每片文档该挂哪些字段逐一列清。
Qdrant 的代价是学习曲线略陡。概念上它讲 points、payload、collection 三件套,初次接触的人要想一会儿。另外采用 Rust 编写虽然性能好,但本地调试排查问题相比 Python 生态少了一些顺手工具。它是一辆正儿八经的旅行车,能跑长途能装货,但你得花点时间熟悉驾驶座。
顺带说两个它特别讨人喜欢的细节。一是写入和查询可以并行,重建索引这种重活儿可以在线做,不停机把向量库整体更新一遍,对于七乘二十四小时的服务来说是刚需。二是它的分页 API 设计得克制,深翻数据时不会像某些库那样翻着翻着超时。这些细节单看不起眼,真到了运维阶段每一条都能救你一次。
怎么选:一张表加一个决策树
这两个数据库我都实际跑过,直接给结论式的对照:
| 维度 | Chroma | Qdrant |
|---|---|---|
| 安装门槛 | 极低,pip 即用 | 需 Docker 或二进制 |
| 默认端口 | 8000 | 6333 与 6334 |
| 开发语言 | Python 与 JS | 多语言客户端齐全 |
| 百万级向量性能 | 明显下降 | 依然稳定 |
| 元数据过滤 | 简单条件为主 | 强,可深度组合 |
| gRPC 支持 | 无 | 有 |
| 内置 embedding | 有,默认 all-MiniLM | 无,需外部计算 |
| 适合场景 | 原型、小团队、几十万片 | 生产、过滤密集、大规模 |
决策其实就三问。第一问,你的文档规模能到百万片吗?能达到或预计会到,直接 Qdrant,别犹豫。第二问,检索前要不要做复杂过滤?比如按部门、密级、时间窗口组合筛选,要的话 Qdrant 的过滤与检索融合设计省心得多。第三问,团队是不是纯 Python 且就想快点看到效果?前两问都答否,那 Chroma 让你周五下班前就能跑出演示版。
还有个实用建议:别把宝押死。两个库都支持导出与重建,前期用 Chroma 快速验证检索效果,等数据量或过滤需求上来再迁到 Qdrant,迁移工作量主要是重新灌入向量,一晚上能搞定。要是你打算一步到位省麻烦,Qdrant 起手也无妨,它的客户端 API 并不复杂,学习成本远低于它带来的那些收益。
迁移这事多说两句。核心动作就是从旧库批量读出向量与元数据,按新库的格式写回去,别用任何厂商锁定的特性就能保证这一步总是走得通。我自己的做法是灌库脚本里留一个抽象层,读写接口统一,底下换库只改一个适配器文件,不到两百行代码。这种设计在 ol-0405 讲成本时会再提一次,硬件和软件的替换成本都该提前算进总账里。
如果你对怎么把文档切片、怎么往库里写这些事还犯怵,下一步看 ol-0304,它把切片、检索、拼接提示一整条流水线拆开讲,正好衔接这里。至于想直接用现成平台把整条链路串起来的读者,ol-0305 比从零手撸要省力得多。
常见问题
Chroma 和 Qdrant 能在同一台机器上同时跑吗
能,端口不冲突。Chroma 占 8000,Qdrant 占 6333 和 6334,互不干扰。常见做法是开发机上用 Chroma 做原型,同时跑个 Qdrant 实例做对比评测,同一批文档各灌一份,对比两者的检索质量。注意内存占用,十六 GB 的机器同时带两个库会比较紧张。
Docker 重启后向量库数据会丢吗
只要挂载了卷就不会。Chroma 把数据写在容器的 /chroma/chroma 目录,Qdrant 写在 /qdrant/storage,用 -v 参数映射到宿主机即可。要是忘了挂载就重启容器,数据确实会没——这个亏吃过一回,灌了十万片文档一晚上白干。养成习惯:docker run 的时候先把 -v 参数写上再考虑别的。
检索结果里有明显不相关的片段怎么办
先别怪数据库。八成是三件事之一:embedding 模型不匹配你的文档语种;切片太大导致一片里混进多个主题,相似度自然不干净;过滤条件没生效,把别的业务线的文档也召回了。排查顺序是先打印每片的相似度分数看看分布,再检查切片策略,最后核对过滤条件。切片调优的具体手法在流程那篇里有展开,里面讲了为什么每片控制在五百字上下是个常见的好起点。
这两个库哪个更省内存
同等数据量下 Qdrant 略省一些,Rust 的内存管理加上可选的量化存储,百万片 768 维向量配八个 G 内存能跑得动。Chroma 在同等规模下要宽裕一些,建议留出十六 GB。不过上规模以后两者都得开量化或者降维,这才是内存大户真正的解法,光换库省不了多少。
两个库都支持像关系库那样做关联查询吗
都不支持,设计上也不打算支持。向量数据库的定位是相似度检索专用件,跨表关联这类需求应该放在你的应用层做。典型的组合是知识库的文档元数据另存一份在关系库或者 JSON 文件里,向量库里只存向量加必要的过滤字段,两边以文档 ID 对上。这种各管一段的做法反而清晰,硬要在向量库里模拟关联功能,写出来的代码既慢又难维护。
小公司刚起步,有没有更省事的选择
有,sqlite 加向量扩展这类方案这两年也成熟了,一个文件就是整个库,连 Docker 都不用跑。文档量在几万片以内、就一个应用进程访问时,它比 Chroma 还轻。但相应地,并发能力、过滤能力和迁移工具链都弱一截。拿它做个人知识管理工具合适,做企业级知识库建议还是直接上 Chroma 或 Qdrant,别过早把自己锁死在玩具方案上。
