分布式部署 #

本章介绍 Qdrant 的分布式架构和高可用部署方案。

分布式架构概述 #

text
Qdrant 分布式架构:

┌─────────────────────────────────────────────────────────────┐
│                      Client Layer                            │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  REST API   │  │   gRPC      │  │    SDK      │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
├─────────────────────────────────────────────────────────────┤
│                    Consensus Layer                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              Raft Consensus                          │   │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐             │   │
│  │  │ Leader  │  │Follower │  │Follower │             │   │
│  │  └─────────┘  └─────────┘  └─────────┘             │   │
│  └─────────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────┤
│                     Data Layer                               │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   Shard 1   │  │   Shard 2   │  │   Shard 3   │         │
│  │  ┌───┬───┐  │  │  ┌───┬───┐  │  │  ┌───┬───┐  │         │
│  │  │ R │ R │  │  │  │ R │ R │  │  │  │ R │ R │  │         │
│  │  │ 1 │ 2 │  │  │  │ 1 │ 2 │  │  │  │ 1 │ 2 │  │         │
│  │  └───┴───┘  │  │  └───┴───┘  │  │  └───┴───┘  │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

核心概念 #

分片(Sharding) #

text
分片策略:

Collection 数据分布:

┌─────────────────────────────────────────────────────────────┐
│                    Collection                                │
│  总数据:100 万向量                                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Shard 1          Shard 2          Shard 3                  │
│  33 万向量        33 万向量        34 万向量                 │
│  ┌─────────┐     ┌─────────┐     ┌─────────┐               │
│  │ Node A  │     │ Node B  │     │ Node C  │               │
│  └─────────┘     └─────────┘     └─────────┘               │
│                                                              │
└─────────────────────────────────────────────────────────────┘

分片优势:
├── 水平扩展
├── 并行查询
└── 负载均衡

复制(Replication) #

text
复制策略:

每个分片有多个副本:

┌─────────────────────────────────────────────────────────────┐
│                      Shard 1                                 │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Replica 1        Replica 2        Replica 3                │
│  (Leader)         (Follower)       (Follower)               │
│  ┌─────────┐     ┌─────────┐     ┌─────────┐               │
│  │ Node A  │     │ Node B  │     │ Node C  │               │
│  └─────────┘     └─────────┘     └─────────┘               │
│                                                              │
│  写操作 → Leader                                              │
│  读操作 → 任意副本                                            │
│                                                              │
└─────────────────────────────────────────────────────────────┘

复制优势:
├── 数据冗余
├── 高可用
└── 读写分离

一致性级别 #

text
一致性级别:

强一致性(Strong):
├── 等待所有副本确认
├── 最高数据可靠性
└── 最低性能

多数一致性(Majority):
├── 等待多数副本确认
├── 平衡可靠性和性能
└── 推荐默认选择

弱一致性(Weak):
├── 不等待副本确认
├── 最高性能
└── 可能数据丢失

集群部署 #

Docker Compose 部署 #

yaml
version: '3.8'

services:
  qdrant-node-1:
    image: qdrant/qdrant:latest
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - ./node1/storage:/qdrant/storage
    environment:
      - QDRANT__CLUSTER__ENABLED=true
      - QDRANT__CLUSTER__P2P__PORT=6335
    command: ./qdrant --uri "http://qdrant-node-1:6335"

  qdrant-node-2:
    image: qdrant/qdrant:latest
    ports:
      - "6336:6333"
      - "6337:6334"
    volumes:
      - ./node2/storage:/qdrant/storage
    environment:
      - QDRANT__CLUSTER__ENABLED=true
      - QDRANT__CLUSTER__P2P__PORT=6335
    command: ./qdrant --uri "http://qdrant-node-2:6335"

  qdrant-node-3:
    image: qdrant/qdrant:latest
    ports:
      - "6338:6333"
      - "6339:6334"
    volumes:
      - ./node3/storage:/qdrant/storage
    environment:
      - QDRANT__CLUSTER__ENABLED=true
      - QDRANT__CLUSTER__P2P__PORT=6335
    command: ./qdrant --uri "http://qdrant-node-3:6335"

Kubernetes 部署 #

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: qdrant-cluster
spec:
  serviceName: qdrant-headless
  replicas: 3
  selector:
    matchLabels:
      app: qdrant
  template:
    metadata:
      labels:
        app: qdrant
    spec:
      containers:
      - name: qdrant
        image: qdrant/qdrant:latest
        ports:
        - containerPort: 6333
          name: http
        - containerPort: 6334
          name: grpc
        - containerPort: 6335
          name: p2p
        env:
        - name: QDRANT__CLUSTER__ENABLED
          value: "true"
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        command:
        - ./qdrant
        - --uri
        - "http://$(POD_IP):6335"
        volumeMounts:
        - name: data
          mountPath: /qdrant/storage
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 50Gi
---
apiVersion: v1
kind: Service
metadata:
  name: qdrant-headless
spec:
  clusterIP: None
  selector:
    app: qdrant
  ports:
  - port: 6333
    name: http
  - port: 6334
    name: grpc
  - port: 6335
    name: p2p
---
apiVersion: v1
kind: Service
metadata:
  name: qdrant
spec:
  selector:
    app: qdrant
  ports:
  - port: 6333
    name: http
    targetPort: 6333
  - port: 6334
    name: grpc
    targetPort: 6334
  type: ClusterIP

Helm 部署 #

bash
helm repo add qdrant https://qdrant.github.io/qdrant-helm
helm repo update

helm install qdrant qdrant/qdrant \
    --set replicaCount=3 \
    --set cluster.enabled=true \
    --set persistence.size=100Gi \
    --set resources.requests.memory=8Gi \
    --set resources.requests.cpu=2

集群管理 #

创建集群 Collection #

python
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams

client = QdrantClient(url="http://localhost:6333")

client.create_collection(
    collection_name="distributed_collection",
    vectors_config=VectorParams(size=384, distance=Distance.COSINE),
    shard_number=3,
    replication_factor=2,
    write_consistency_factor=1
)

print("分布式 Collection 创建成功")

集群参数说明 #

参数 说明 推荐值
shard_number 分片数量 节点数或其倍数
replication_factor 副本数量 2-3
write_consistency_factor 写确认副本数 1-2
read_consistency_factor 读确认副本数 1

查看集群状态 #

python
cluster_info = client.get_cluster_info()

print(f"集群状态: {cluster_info.status}")
print(f"节点数量: {cluster_info.number_of_peers}")
print(f"Raft 状态: {cluster_info.raft_info}")

for peer in cluster_info.peers:
    print(f"节点: {peer.uri}")
    print(f"  状态: {peer.state}")

查看分片分布 #

python
collection_info = client.get_collection("distributed_collection")

for shard in collection_info.shards:
    print(f"分片 {shard.shard_id}:")
    print(f"  状态: {shard.state}")
    print(f"  节点: {shard.peer_id}")

数据迁移 #

集群引导 #

python
from qdrant_client.models import PeerInfo

client.add_peer(
    uri="http://new-node:6335"
)

print("新节点已加入集群")

移除节点 #

python
client.remove_peer(
    peer_id=2
)

print("节点已从集群移除")

数据重平衡 #

python
client.update_collection(
    collection_name="distributed_collection",
    optimizer_config=OptimizersConfigDiff(
        deleted_threshold=0.2
    )
)

高可用配置 #

多可用区部署 #

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: qdrant-cluster
spec:
  serviceName: qdrant-headless
  replicas: 6
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchLabels:
                app: qdrant
            topologyKey: topology.kubernetes.io/zone
      containers:
      - name: qdrant
        image: qdrant/qdrant:latest

健康检查配置 #

yaml
livenessProbe:
  httpGet:
    path: /health
    port: 6333
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 6333
  initialDelaySeconds: 5
  periodSeconds: 5

负载均衡配置 #

yaml
apiVersion: v1
kind: Service
metadata:
  name: qdrant-lb
spec:
  selector:
    app: qdrant
  ports:
  - port: 6333
    targetPort: 6333
  type: LoadBalancer

一致性配置 #

写一致性 #

python
from qdrant_client.models import WriteOrdering

client.upsert(
    collection_name="distributed_collection",
    points=[point],
    wait=True,
    ordering=WriteOrdering.STRONG
)

读一致性 #

python
from qdrant_client.models import ReadConsistency

results = client.search(
    collection_name="distributed_collection",
    query_vector=[0.1] * 384,
    consistency=ReadConsistency.MAJORITY
)

监控和运维 #

集群健康检查 #

python
def check_cluster_health():
    try:
        info = client.get_cluster_info()
        
        if info.status != "enabled":
            return {"status": "unhealthy", "reason": "Cluster not enabled"}
        
        for peer in info.peers:
            if peer.state != "Active":
                return {"status": "degraded", "reason": f"Peer {peer.peer_id} not active"}
        
        return {"status": "healthy"}
    
    except Exception as e:
        return {"status": "error", "reason": str(e)}

health = check_cluster_health()
print(f"集群健康状态: {health}")

分片状态监控 #

python
def monitor_shards(collection_name):
    info = client.get_collection(collection_name)
    
    shards_status = {
        "green": 0,
        "yellow": 0,
        "red": 0
    }
    
    for shard in info.shards:
        if shard.state == "Active":
            shards_status["green"] += 1
        elif shard.state == "Partial":
            shards_status["yellow"] += 1
        else:
            shards_status["red"] += 1
    
    return shards_status

status = monitor_shards("distributed_collection")
print(f"分片状态: {status}")

故障恢复 #

节点故障处理 #

text
节点故障恢复流程:

1. 检测故障
   └── 心跳超时

2. 重新选举
   └── Raft 选举新 Leader

3. 数据同步
   └── 从其他副本同步数据

4. 恢复服务
   └── 节点重新加入集群

数据恢复 #

python
def recover_collection(collection_name):
    info = client.get_collection(collection_name)
    
    if info.status == "red":
        print(f"Collection {collection_name} 需要恢复")
        
        client.update_collection(
            collection_name=collection_name,
            optimizer_config=OptimizersConfigDiff(
                deleted_threshold=0.1
            )
        )

recover_collection("distributed_collection")

最佳实践 #

集群规模建议 #

text
集群规模建议:

小规模(< 100 万向量):
├── 节点数:3
├── 分片数:3
├── 副本数:2
└── 单节点配置:4C8G

中等规模(100-1000 万向量):
├── 节点数:5
├── 分片数:5
├── 副本数:2
└── 单节点配置:8C16G

大规模(> 1000 万向量):
├── 节点数:7+
├── 分片数:节点数 × 2
├── 副本数:3
└── 单节点配置:16C32G+

网络配置 #

yaml
network:
  bind: 0.0.0.0
  
cluster:
  enabled: true
  p2p:
    port: 6335
    connection_pool_size: 20
  consensus:
    tick_period_ms: 100
    heartbeat_timeout_ms: 500

小结 #

本章详细介绍了分布式部署:

  • 分布式架构原理
  • 分片和复制策略
  • 集群部署方法
  • 高可用配置
  • 监控和故障恢复

下一步 #

掌握分布式部署后,继续学习 快照与备份,了解数据备份和恢复策略!

最后更新:2026-04-04