Qdrant向量数据库核心概念
参考:
Qdrant官网
Embedding(向量化)
Embedding = 把一段文字(或图片/音频)转换成一个固定长度的数字数组(向量),比如 [0.02, -0.13, 0.87, ...],通常是 384、768、1536 维。
这个转换由一个专门训练过的模型完成(如 OpenAI 的 text-embedding-3、BGE、M3E),不是简单的编码/加密。
转换的核心规则:语义相近的文字 → 向量在空间中的位置也相近。
为什么"挠家具"和"抓沙发"字面不同,向量却能接近?
因为 Embedding 模型是在海量文本上训练的,学到了"挠"和"抓"、"家具"和"沙发"在大量上下文中经常互换使用、含义相近。模型把这种"共现规律"编码进了向量的每一维数字里——你可以粗略理解成,向量的每一维代表某种隐含的语义特征(不是人能直接读懂的具体维度,而是模型学出来的抽象特征)。
原始文字
↓ (Embedding 模型, 如 BGE/OpenAI)
向量 [0.02, -0.13, ...]
↓ (存入)
向量数据库 (Qdrant的Collection)
↓ (查询时)
用户问题 → 同一个Embedding模型 → 查询向量
↓
在数据库里找"距离最近"的存储向量
↓
返回对应的原文
Embedding 模型 ≠ 向量数据库。Embedding 模型(如 BGE)负责"把文字变成向量",Qdrant 只负责"存向量+快速找最近的向量",两者是分开的组件,Qdrant 本身不做向量化。
向量数据库不存"文字的意思",存的就是一堆数字数组,"意思"隐含在数字的相对位置关系里。
Qdrant 核心概念
Collection(集合)= 存放一类向量的容器,类似关系数据库里的"表"。一个 Collection 里的所有向量必须维度相同、用同一种距离度量
Point(点)= Collection 里的一条记录,结构是 {id, vector, payload}
Distance(距离度量)= 判断两个向量"有多相似"的计算方式,Qdrant 支持三种:
Cosine(余弦相似度)—— 只看方向,不看长度,文本 Embedding 最常用
Euclidean(欧氏距离)—— 看空间直线距离
Dot(点积)—— 同时受方向和长度影响,某些模型(如做过归一化的)会指定用它
创建 Collection 时必须指定:向量维度(跟你用的 Embedding 模型输出维度一致,比如 BGE-base 是 768 维)+ 距离度量方式
为什么 Collection 要求维度和距离度量统一?
因为"距离计算"本身是数学运算,必须在同一个坐标系下才有意义——你不能拿一个 768 维的向量去跟一个 1536 维的向量算余弦相似度。同理,如果一部分数据用 Cosine 存、一部分用 Euclidean 算,"距离"这个概念就失去了统一标准,排序结果没有可比性。这也意味着:一旦你换了 Embedding 模型(维度变了),旧 Collection 里的向量就不能直接复用了,得重新向量化。
Qdrant 实例 (一个数据库服务)
│
├── Collection A (维度768, Cosine) ← 比如"产品文档"
│ ├── Point 1 {id, vector, payload}
│ ├── Point 2 {id, vector, payload}
│ └── ...
│
└── Collection B (维度1536, Cosine) ← 比如"客服对话"
└── ...Collection ≠ 数据库(Database)。Qdrant 一个实例本身相当于"数据库",Collection 才对应"表"
维度不是随便定的,必须和你选用的 Embedding 模型输出维度严格一致,写错了插入数据会直接报错
Payload 过滤 (Filter)
Filter 语法核心三件套:must(必须满足,相当于 AND)、should(满足其一即可,相当于 OR)、must_not(必须不满足)
过滤和向量搜索同时发生,不是"先搜后筛",这样即使目标数据向量相似度不是最高,只要满足过滤条件,也不会被漏掉
常用条件类型:MatchValue(精确匹配)、Range(数值范围,如年份/价格区间)、MatchAny(多值匹配,如标签在某个列表里)
用户查询:"2024年的财务政策"
│
├─→ 向量化 → 查询向量 → 语义相似度计算
│
└─→ 解析出过滤条件 → year = 2024
│
└─→ Qdrant 同时应用两者,在"year=2024"的子集内做向量近邻搜索
│
└─→ 返回 Top-Kfrom qdrant_client.models import Filter, FieldCondition, MatchValue
results = client.search(
collection_name="my_docs",
query_vector=[0.03, -0.11, 0.85, ...], # "2024年财务政策"的查询向量
query_filter=Filter(
must=[
FieldCondition(key="year", match=MatchValue(value=2024))
]
),
limit=5
)Filter 不是在应用层写 if 判断筛选,而是传给 Qdrant 服务端,由数据库在检索阶段就应用(性能更好,且不会漏掉排名靠后但符合条件的结果)
payload 字段要能被高效过滤,最好提前建索引(Qdrant 里叫 Payload Index),不建索引在数据量大时过滤会变慢——这是进阶话题,先记住有这个概念
混合检索 (Hybrid Search)
混合检索 = 稠密向量(Dense Vector)+ 稀疏向量(Sparse Vector) 两种检索方式的结果融合,取长补短
稠密向量(我们之前一直在讲的)—— 每一维都有值(如768维全是数字),擅长捕捉语义相似("挠家具"≈"抓沙发")
稀疏向量 —— 大部分维度是0,只有少数维度有值,本质上类似升级版的关键词匹配(如 BM25),擅长捕捉精确词面匹配(比如专有名词、型号、产品代码这类"必须原词命中"的场景)
Qdrant 的 1.10 版本引入了统一的 Query API,把稠密向量、稀疏向量、多向量等不同检索方式整合进同一个查询端点,支持在服务器端组合出混合搜索 Qdrant
为什么单用稠密向量不够?
举个例子:用户搜"Qdrant v1.10 更新了什么",稠密向量搜索理解"更新了什么"这种语义没问题,但对"v1.10"这种具体版本号,语义模型未必能精确区分"v1.10"和"v1.9"——它们在语义空间里可能离得很近(都是"版本号"这个概念),但用户明明要的是精确匹配。这时候纯语义搜索会失真,而传统关键词/稀疏向量正好擅长处理这种"必须原词命中"的情况。
反过来,纯稀疏向量(关键词)搜不出"挠家具"→"抓沙发"这种同义改写。所以两者结合,互补短板。
用户查询
│
├─→ 稠密Embedding模型 → 稠密向量 → 语义相似度搜索
│
└─→ 稀疏Embedding模型(如BM25/SPLADE) → 稀疏向量 → 关键词精确匹配
│
└─→ 两路结果 → 融合算法(RRF 或 DBSF) → 最终排序结果Qdrant 支持用 RRF(Reciprocal Rank Fusion)或 DBSF 融合稠密、稀疏、多向量的检索结果,也可以用 Formula Query 自定义打分逻辑:
RRF:不看两路各自的分数具体多大,只看"排名",把两路结果按排名做加权合并——简单粗暴但很稳健,是最常用的默认选择
DBSF:会考虑分数分布,做更精细的归一化融合
混合检索(同一 Collection 内融合稠密+稀疏)≠ 之前提到的"跨 Collection 联合查询"(Qdrant 不原生支持后者,混合检索讲的是前者)
稀疏向量 ≠ 稠密向量降维压缩。稀疏向量是完全不同的表示方式(基于词频/词重要性),不是把768维向量简单变"稀疏"
from qdrant_client.models import Prefetch, FusionQuery, Fusion
results = client.query_points(
collection_name="my_docs",
prefetch=[
Prefetch(query=dense_vector, using="dense", limit=20), # 稠密向量召回
Prefetch(query=sparse_vector, using="sparse", limit=20), # 稀疏向量召回
],
query=FusionQuery(fusion=Fusion.RRF), # 融合两路结果
limit=5
)Qdrant向量数据库核心概念
https://dhc.ink/archives/qdrantxiang-liang-shu-ju-ku
评论