共计 2165 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点:为什么需要向量数据库?
最近几年,非结构化数据(如图片、音频、文本)的爆炸式增长让传统关系型数据库捉襟见肘。想象一下,当你要从 100 万张图片中找出相似的猫咪照片时,用 SQL 写 WHERE 条件来比较像素?这显然行不通。

传统数据库的局限性主要体现在:
- 无法高效计算相似度:向量间的余弦相似度或欧氏距离计算需要特殊索引
- 维度灾难:处理 512 维以上的向量时,B 树索引完全失效
- 实时性差:LIKE 查询在亿级数据中可能需要分钟级响应
这就是为什么我们需要专门的向量数据库——它们为高维向量设计了专属的存储和检索算法(如 ANN- 近似最近邻),让相似性搜索变得可行。
技术选型:Chroma vs Qdrant
架构对比
| 特性 | Chroma | Qdrant |
|---|---|---|
| 定位 | 轻量级嵌入式 | 分布式生产级 |
| 存储引擎 | SQLite/ClickHouse | 自研 Rust 引擎 |
| 通信协议 | HTTP/REST | gRPC + REST |
| 扩展性 | 单机 | 支持分片与副本 |
性能测试(基于 LAION-5B 数据集子集)
测试环境:AWS c5.2xlarge, 768 维向量
| 指标 | Chroma | Qdrant |
|---------------|-----------|-----------|
| 写入吞吐(QPS) | 1,200 | 8,500 |
| 查询延迟(P99) | 45ms | 12ms |
| 内存占用 | 2GB | 6GB |
如何选择?
- 选 Chroma:快速原型开发、本地测试、小数据集(<100 万向量)
- 选 Qdrant:需要水平扩展、生产环境部署、超大规模数据
实战演练
Chroma 快速上手
import chromadb
# 1. 创建内存型客户端
client = chromadb.Client()
# 2. 创建集合(相当于表)collection = client.create_collection("images")
# 3. 添加向量(自动生成 ID)collection.add(embeddings=[[0.1, 0.2, ...], [0.3, 0.5, ...]], # 你的向量列表
metadatas=[{"category": "cat"}, {"category": "dog"}],
documents=["cat_image.jpg", "dog_image.png"] # 原始数据
)
# 4. 相似性查询
results = collection.query(query_embeddings=[[0.15, 0.18, ...]], # 查询向量
n_results=3 # 返回 Top3
)
Qdrant 集群部署
# 使用 Docker Compose 部署 3 节点集群
version: '3'
services:
qdrant1:
image: qdrant/qdrant
ports:
- "6333:6333"
- "6334:6334"
volumes:
- ./data1:/storage
qdrant2:
image: qdrant/qdrant
ports:
- "6335:6333"
volumes:
- ./data2:/storage
depends_on:
- qdrant1
Python 客户端调用:
from qdrant_client import QdrantClient
client = QdrantClient(
host="qdrant1",
port=6333,
grpc_port=6334,
prefer_grpc=True # 高性能 gRPC 模式
)
# 创建分片集合
client.create_collection(
collection_name="products",
vectors_config=VectorParams(size=768, distance=Distance.COSINE)
)
生产环境进阶技巧
索引选择指南
- HNSW:查询快但内存占用高,适合读取密集型场景
- IVF:需要定期训练,适合写入频繁的场景
维度灾难应对
- 优先使用 PCA 降维(保持 95% 方差)
- 对 >1024 维的向量启用标量量化(SQ8)
写入优化
# Chroma 批量写入最佳实践
with client.batch():
for i in range(0, len(vectors), 1000):
collection.add(embeddings=vectors[i:i+1000],
batch_size=1000 # 减少 IO 次数
)
# Qdrant 推荐配置
client.upsert(
collection_name="logs",
points=Batch(ids=range(10000),
vectors=vectors,
),
wait=False # 异步写入
)
安全防护
启用 TLS 加密
# Qdrant 配置示例
service:
enable_tls: true
cert_path: /path/to/cert.pem
key_path: /path/to/key.pem
角色权限控制
# 创建只读 API 密钥
client.create_api_key(
description="frontend-key",
read_only=True
)
延伸思考
- 如何结合传统 SQL 条件过滤(如
price < 100)与向量搜索? - 当标量字段频繁更新时,如何避免重建整个向量索引?
- 在多模态搜索中,能否统一处理文本和图像的嵌入向量?
建议读者用 COCO 或 SIFT1M 等标准数据集复现测试,观察不同参数对 Recall@K 的影响。记住:没有银弹,最适合的才是最好的。
正文完
发表至: 未分类
近两天内
