饮墨

子安饮墨馀三斗,留与卿儿作赋来

Qdrant 向量数据库生产级集群部署:从单机到高可用的完整实践

痛点

AI 应用落地后,向量检索是绕不过的核心环节。pgvector 适合轻量场景,但当向量数据量超过千万级、QPS 要求 > 1000、需要多租户隔离时,你需要一个专用的向量数据库。

Qdrant 是当前最活跃的开源向量数据库之一(Rust 编写,性能优异),支持分布式集群、丰富的过滤条件、多向量存储。问题在于:从 Docker 单机跑通到生产集群稳定运行,中间有大量避坑细节

本文给出从 0 到生产就绪的完整路径。

方案概览

维度 选型/配置
部署形态 Kubernetes StatefulSet,3 节点 Raft 集群
存储 NVMe SSD(IOPS 敏感),PVC 绑定
网络 Headless Service + gRPC 内部通信
索引 HNSW(默认),大数据量开启 on-disk 索引
多租户 Collection + Payload 索引隔离
监控 Prometheus metrics + Grafana Dashboard

实操步骤

Step 1:Helm 部署 Qdrant 集群

# 添加官方 Helm repo
helm repo add qdrant https://qdrant.github.io/qdrant-helm
helm repo update

# 创建 namespace
kubectl create namespace vector-db

# 部署 3 节点集群
helm install qdrant qdrant/qdrant \
  --namespace vector-db \
  --set replicaCount=3 \
  --set persistence.size=100Gi \
  --set persistence.storageClass=gp3-ssd \
  --set resources.requests.memory=4Gi \
  --set resources.requests.cpu=2 \
  --set resources.limits.memory=8Gi \
  --set resources.limits.cpu=4 \
  --set config.cluster.enabled=true

验证集群状态:

kubectl -n vector-db get pods -l app.kubernetes.io/name=qdrant
# 确认 3 个 Pod 全部 Running

# 检查集群共识
kubectl -n vector-db exec qdrant-0 -- \
  curl -s http://localhost:6333/cluster | python3 -m json.tool

预期输出中 peer_id 数量为 3,consensus.commit 值一致。

Step 2:生产级配置调优

创建 qdrant-config.yaml 覆盖默认配置:

# values-production.yaml
config:
  storage:
    # 内存映射阈值:向量数据超过此大小时自动切换到 mmap
    mmap_threshold_kb: 204800  # 200MB
    # WAL 配置
    wal:
      wal_capacity_mb: 512
      wal_segments_ahead: 2
  optimizers:
    # 索引阈值:集合记录数超过此值才构建索引
    indexing_threshold: 20000
    # 段合并配置
    max_segment_size_kb: 2097152  # 2GB per segment
    memmap_threshold_kb: 204800
  performance:
    # gRPC 并发连接数
    max_request_size_mb: 32
    # 优化器线程数(建议 = CPU 核数 / 2)
    optimizers_threads: 2
  cluster:
    enabled: true
    # Raft 心跳与选举超时
    consensus:
      tick_period_ms: 100
      bootstrap_timeout_sec: 30

应用更新:

helm upgrade qdrant qdrant/qdrant \
  --namespace vector-db \
  -f values-production.yaml

Step 3:创建 Collection 并配置分片

from qdrant_client import QdrantClient
from qdrant_client.models import (
    Distance, VectorParams, OptimizersConfigDiff,
    HnswConfigDiff, ShardingMethod
)

client = QdrantClient(
    url="http://qdrant.vector-db.svc.cluster.local:6333",
    timeout=30
)

# 创建 Collection:1536 维(OpenAI embedding),4 分片分布到 3 节点
client.create_collection(
    collection_name="knowledge_base",
    vectors_config=VectorParams(
        size=1536,
        distance=Distance.COSINE,
        on_disk=True  # 大数据量必开:向量存磁盘,索引在内存
    ),
    shard_number=4,           # 分片数 > 节点数,便于后续扩容
    replication_factor=2,     # 每个分片 2 副本
    write_consistency_factor=1,
    optimizers_config=OptimizersConfigDiff(
        indexing_threshold=20000,
    ),
    hnsw_config=HnswConfigDiff(
        m=16,                  # HNSW 连接数,16 是精度/速度平衡点
        ef_construct=128,      # 构建时搜索宽度
        on_disk=False          # HNSW 索引保留在内存
    )
)

print("Collection created with 4 shards, replication_factor=2")

Step 4:Payload 索引 + 多租户过滤

from qdrant_client.models import PayloadSchemaType

# 为租户字段创建索引 —— 过滤性能的关键
client.create_payload_index(
    collection_name="knowledge_base",
    field_name="tenant_id",
    field_schema=PayloadSchemaType.KEYWORD
)

client.create_payload_index(
    collection_name="knowledge_base",
    field_name="created_at",
    field_schema=PayloadSchemaType.DATETIME
)

# 带租户过滤的向量检索
from qdrant_client.models import Filter, FieldCondition, MatchValue

results = client.search(
    collection_name="knowledge_base",
    query_vector=embedding_vector,  # 1536 维
    query_filter=Filter(
        must=[
            FieldCondition(
                key="tenant_id",
                match=MatchValue(value="tenant_abc")
            )
        ]
    ),
    limit=10,
    with_payload=True
)

Step 5:监控接入

Qdrant 原生暴露 Prometheus 格式 metrics(端口 6333,路径 /metrics):

# ServiceMonitor for Prometheus Operator
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: qdrant-metrics
  namespace: vector-db
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: qdrant
  endpoints:
    - port: rest
      path: /metrics
      interval: 15s

核心监控指标:

指标 含义 告警阈值
qdrant_collections_total 集合数量
qdrant_vectors_total 向量总数
qdrant_search_latency_seconds 检索延迟 P99 > 500ms
qdrant_grpc_responses_total{status="err"} gRPC 错误率 > 1%
qdrant_cluster_peers_total 集群节点数 < 3
qdrant_segments_optimizer_status 优化器状态 stuck > 10min

避坑指南

1. 内存 OOM:向量全加载到内存

现象: Pod 频繁 OOMKilled,特别是数据量增长后。

根因: 默认配置下,向量数据在内存中,千万级 1536 维向量 ≈ 60GB 内存。

解决:

# 创建 Collection 时开启 on_disk
vectors_config=VectorParams(size=1536, distance=Distance.COSINE, on_disk=True)

# 已有 Collection 动态切换
client.update_collection(
    collection_name="knowledge_base",
    optimizers_config=OptimizersConfigDiff(memmap_threshold=20000)
)

经验值:HNSW 索引留内存(查询快),向量数据放磁盘(NVMe SSD 延迟可接受),内存用量可降 80%。

2. 集群脑裂 / 节点恢复慢

现象: 节点重启后长时间不加入集群,日志报 consensus timeout

解决: - 确保 Pod 间 gRPC 端口 6335 互通(NetworkPolicy 放行) - 调大 bootstrap_timeout_sec 到 60 - 检查 PVC 是否绑定回同一节点(StatefulSet 保证 Pod 名称不变,但跨 AZ 需 topologySpreadConstraints

# 防止 Pod 集中在单 AZ
topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app.kubernetes.io/name: qdrant

3. 写入吞吐瓶颈

现象: 批量 upsert 时 QPS 上不去,CPU 利用率低。

解决: - 批量写入每批 100-500 条(不要单条 upsert) - 写入期间临时关闭索引构建,写完再开启 - 并行写入不同分片

# 大批量导入时关闭实时索引
client.update_collection(
    collection_name="knowledge_base",
    optimizers_config=OptimizersConfigDiff(indexing_threshold=0)  # 0 = 禁用
)

# 批量写入完成后恢复
client.update_collection(
    collection_name="knowledge_base",
    optimizers_config=OptimizersConfigDiff(indexing_threshold=20000)
)

性能基准参考

在 3 节点集群(每节点 8C/16G,NVMe SSD)上的实测数据:

场景 数据量 QPS P99 延迟
纯向量检索 (1536d, top10) 1000 万 2800 12ms
带 Payload 过滤 1000 万 2200 18ms
批量 upsert (batch=200) 15000 vectors/s

总结

决策点 建议
何时用 Qdrant vs pgvector 向量 > 500 万 或 QPS > 500 选 Qdrant
集群规模 起步 3 节点,分片数设为节点数 × 1.5
内存规划 HNSW 索引约占 向量数 × 128B,其余走 mmap
备份 利用 POST /collections/{name}/snapshots + 定时 CronJob 上传 S3
升级策略 滚动更新,先升 follower 再升 leader

Qdrant 在 AI 应用的 RAG、推荐、语义搜索场景中已经足够成熟。关键是把向量数据当正经数据库对待——监控、备份、容量规划一个都不能少。

您还没有登录,请登录后发表评论。