Qdrant:高性能向量搜索引擎

FreeGuideOnline 最新 2026-07-02

bash docker pull qdrant/qdrant docker run -p 6333:6333 -p 6334:6334
-v $(pwd)/qdrant_storage:/qdrant/storage:z
qdrant/qdrant


启动后,你可以通过以下地址访问服务:

- REST API:`http://localhost:6333`  
- Web UI(便捷调试面板):`http://localhost:6333/dashboard`  
- gRPC API:`http://localhost:6334`(高性能场景使用)

> 如果你不想安装任何东西,也可以使用 Qdrant 官方提供的 [在线试用环境](https://cloud.qdrant.io)。

本教程以 REST API 为例进行演示(也可使用 Python、JavaScript 等客户端库,基本原理相同)。

## 核心概念速览

在开始实战之前,先理解这几个名词:

- **Collection(集合)**:类似关系型数据库中的表,用于组织一组向量数据。集合定义时需指定向量的维度和距离度量方式。
- **Point(点)**:集合中的一条记录,包含:
  - `id`:唯一标识(数字或 UUID)
  - `vector`:浮点数数组,维度必须与集合定义一致
  - `payload`(可选):任意 JSON 对象,存储与该点关联的业务属性(如标题、价格、类别)
- **距离度量**:计算两个向量相似程度的算法,Qdrant 支持:
  - `Cosine`(余弦相似度)
  - `Euclid`(欧几里得距离)
  - `Dot`(点积,常用于已归一化的向量)

## 实战:构建一个简单的书籍语义搜索

假设我们有一个书籍数据库,每本书对应一个意义向量(由某种语言模型生成),我们想基于用户查询的向量找到语义最接近的书,并且可以按类别、价格过滤。

### 1. 创建 Collection

首先创建一个名为 `books` 的集合,向量维度为 384(如果你用 all-MiniLM-L6-v2 这类模型,输出就是 384 维),使用余弦相似度。

```bash
curl -X PUT 'http://localhost:6333/collections/books' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "vectors": {
      "size": 384,
      "distance": "Cosine"
    }
  }'

参数解释:size 指定向量维度,distance 指定距离度量。创建成功后,你会收到包含 "status":"ok" 的响应。

2. 插入向量数据

现在插入几个点。每个点包含一个 384 维向量(此处为了演示,我们用随机小数代替,实际应使用模型提取的 embeddings),以及描述书籍的 payload。

curl -X PUT 'http://localhost:6333/collections/books/points' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "points": [
      {
        "id": 1,
        "vector": [0.05, 0.13, -0.22, ...],  // 此处补全384个浮点数
        "payload": {
          "title": "Deep Learning",
          "author": "Ian Goodfellow",
          "category": "AI",
          "price": 49.99
        }
      },
      {
        "id": 2,
        "vector": [0.02, -0.10, 0.18, ...],
        "payload": {
          "title": "The Great Gatsby",
          "author": "F. Scott Fitzgerald",
          "category": "Fiction",
          "price": 9.99
        }
      }
    ]
  }'

实际开发中,向量通常由模型批量生成,这里省略具体数值。你可以使用 Qdrant Python 客户端 更方便地上传数据:client.upsert(collection_name, points)

3. 执行向量搜索

假设我们现在有一段查询文本 “machine learning for beginners”,经过嵌入模型得到查询向量 query_vector。我们想找与其最相似的 3 本书,并希望只返回类别为 “AI” 且价格小于 60 的结果。

curl -X POST 'http://localhost:6333/collections/books/points/search' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "vector": [0.01, -0.05, 0.12, ...],  // 查询向量
    "limit": 3,
    "with_payload": true,
    "filter": {
      "must": [
        { "key": "category", "match": { "value": "AI" } },
        { "key": "price", "range": { "lt": 60 } }
      ]
    }
  }'

响应示例:

{
  "result": [
    {
      "id": 1,
      "score": 0.92,
      "payload": {
        "title": "Deep Learning",
        "category": "AI",
        "price": 49.99
      }
    }
    // ... 其他相似结果
  ],
  "status": "ok",
  "time": 0.003
}

score 反映了相似度,值越接近 1 表示越相似(余弦相似度)。过滤条件可以在 filter 中灵活组合 mustshouldmust_not

进阶功能:索引优化与负载管理

选择向量索引

Qdrant 默认使用 HNSW(分层可导航小世界图) 索引,这是一种近似最近邻算法,能够在搜索速度与精度间取得良好平衡。你可以在创建集合时自定义 HNSW 参数:

{
  "vectors": { "size": 384, "distance": "Cosine" },
  "hnsw_config": {
    "m": 16,            // 每个节点的最大连接数,影响内存使用和搜索速度
    "ef_construct": 100 // 构建索引时的搜索宽度
  }
}
  • m 越大,索引质量越好,但内存占用越大。
  • ef_construct 越高,索引构建时间越长,但搜索效果更优。

使用 Payload 索引加速过滤

默认情况下,对 payload 字段的过滤是扫描匹配。在数据量大且频繁按某些字段过滤时,可以为这些字段创建索引:

curl -X PUT 'http://localhost:6333/collections/books/index' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "field_name": "category",
    "field_type": "keyword"
  }'

支持的字段类型包括 keywordintegerfloatgeo 等。创建索引后,过滤性能会显著提升。

批量操作与性能调优

  • 使用 Upsert 代替逐个插入,以减少网络开销。
  • 调整 search 请求中的 params 字段,可临时修改搜索参数,例如 "hnsw_ef": 128 可以提高召回率,但耗时也会增加。
  • 对于超大数据集,可启用 磁盘索引(Disk Index),降低内存占用,牺牲少量性能。

客户端库快速集成

对于 Python 开发者,安装官方客户端:

pip install qdrant-client

创建客户端并执行搜索:

from qdrant_client import QdrantClient
from qdrant_client.models import Filter, FieldCondition, MatchValue, Range

client = QdrantClient(host="localhost", port=6333)
# 搜索
results = client.search(
    collection_name="books",
    query_vector=query_embedding,
    limit=3,
    query_filter=Filter(
        must=[
            FieldCondition(key="category", match=MatchValue(value="Fiction")),
            FieldCondition(key="price", range=Range(lt=20))
        ]
    )
)