1. 向量数据库与 Elasticsearch 实践指南
在人工智能和大数据应用日益普及的今天,向量数据库(Vector Database)成为支撑语义搜索、推荐系统、图像检索等场景的关键基础设施。本文将系统介绍向量相似性度量方法、近似最近邻(ANN)搜索原理,并通过完整的 Elasticsearch(ES)操作示例,展示如何构建一个可运行的向量检索系统。
1.1 向量相似性度量方法
向量数据库的核心在于高效计算高维向量之间的相似性。常见的度量方式包括欧氏距离、点积和余弦相似度。
1.1.1 L2 范数与欧氏距离
L2 范数(即欧几里得范数)定义为向量各分量平方和的平方根:
∥x∥2=∑i=1nxi2 |\mathbf{x}|2 = \sqrt{\sum^n x_i^2} ∥x∥2=i=1∑nxi2
两个向量 a\mathbf{a}a 和 b\mathbf{b}b 之间的欧氏距离为:
d(a,b)=∥a−b∥2=∑i=1n(ai−bi)2 d(\mathbf{a}, \mathbf{b}) = |\mathbf{a} - \mathbf{b}|2 = \sqrt{\sum^n (a_i - b_i)^2} d(a,b)=∥a−b∥2=i=1∑n(ai−bi)2
该距离反映的是向量在空间中的绝对位置差异。
1.1.2 点积(Dot Product)
点积是两个向量对应元素相乘后求和:
a⋅b=∑i=1naibi \mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^n a_i b_i a⋅b=i=1∑naibi
点积值受向量长度影响较大,相同方向但不同模长的向量点积可能差异显著。
1.1.3 余弦相似度(Cosine Similarity)
余弦相似度衡量两个向量的方向一致性,忽略其模长:
cosine(a,b)=a⋅b∥a∥2∥b∥2 \text{cosine}(\mathbf{a}, \mathbf{b}) = \frac{\mathbf{a} \cdot \mathbf{b}}{|\mathbf{a}|_2 |\mathbf{b}|_2} cosine(a,b)=∥a∥2∥b∥2a⋅b
其取值范围为 [−1,1][-1, 1][−1,1],值越大表示方向越接近。一个重要性质是:当两个向量均经过 L2 归一化(即模长为 1)后,其余弦相似度等于点积 。这一性质被广泛用于优化计算效率。
1.1.4 Python 实现示例
import numpy as np
from sklearn.preprocessing import normalize
a = np.array([3.0, 4.0])
b = np.array([1.0, 2.0])
# L2 范数
l2_a = np.linalg.norm(a)
# 欧氏距离
euclidean_dist = np.linalg.norm(a - b)
# 点积
dot_product = np.dot(a, b)
# 余弦相似度(手动)
cos_sim = dot_product / (np.linalg.norm(a) * np.linalg.norm(b))
# L2 归一化后,点积等于余弦相似度
a_norm = normalize(a.reshape(1, -1), norm='l2').flatten()
b_norm = normalize(b.reshape(1, -1), norm='l2').flatten()
cos_via_dot = np.dot(a_norm, b_norm)
print(cos_via_dot)
0.9838699100999074
1.2 近似最近邻(ANN)搜索
在高维空间中,精确计算最近邻(KNN)的时间复杂度为 O(Nd)O(Nd)O(Nd),其中 NNN 为向量数量,ddd 为维度。当数据规模达到百万甚至十亿级时,精确搜索难以满足实时性要求。
近似最近邻(Approximate Nearest Neighbor, ANN)算法通过牺牲少量精度换取数量级的性能提升。主流 ANN 算法包括:
- HNSW (Hierarchical Navigable Small World):基于图结构,召回率高,适合中等规模数据;
- IVF (Inverted File Index):先聚类再局部搜索,适合超大规模;
- LSH (Locality-Sensitive Hashing):基于哈希,速度快但精度较低。
Elasticsearch 自 8.0 版本起内置 HNSW 算法,通过 dense_vector 字段的索引功能支持高效 ANN 搜索。
1.3 Elasticsearch 向量检索实战
以下演示如何在本地 Elasticsearch 实例中构建完整的向量索引、插入数据并执行相似性查询。假设 ES 已运行在 http://localhost:9200,用户名为 elastic,密码为 123456。
1.3.1 创建向量索引
首先定义索引映射,包含一个 4 维的 dense_vector 字段,并启用 HNSW 索引:
curl -sS -u elastic:123456 -X PUT "http://localhost:9200/user" \
-H "Content-Type: application/json" \
-d '{
"mappings": {
"properties": {
"name": { "type": "keyword" },
"interest_vector": {
"type": "dense_vector",
"dims": 4,
"index": true,
"similarity": "cosine"
}
}
}
}'
关键参数说明:
dims: 向量维度,必须与实际数据一致;index: true: 启用 HNSW 索引以支持 ANN;similarity: "cosine": 使用余弦相似度,要求写入前对向量进行 L2 归一化。
1.3.2 插入归一化后的向量数据
构造三个 L2 归一化的 4 维向量并插入:
# 用户 Alice
curl -sS -u elastic:123456 -X POST "http://localhost:9200/user/_doc/1" \
-H "Content-Type: application/json" \
-d '{
"name": "Alice",
"interest_vector": [0.6, 0.8, 0.0, 0.0]
}'
# 用户 Bob
curl -sS -u elastic:123456 -X POST "http://localhost:9200/user/_doc/2" \
-H "Content-Type: application/json" \
-d '{
"name": "Bob",
"interest_vector": [0.0, 0.0, 0.7071, 0.7071]
}'
# 用户 Charlie
curl -sS -u elastic:123456 -X POST "http://localhost:9200/user/_doc/3" \
-H "Content-Type: application/json" \
-d '{
"name": "Charlie",
"interest_vector": [0.5, 0.5, 0.5, 0.5]
}'
所有向量均满足 ∥v∥2=1|\mathbf{v}|_2 = 1∥v∥2=1,符合余弦相似度使用要求。
1.3.3 执行 KNN 相似性搜索
以 Alice 的向量为查询条件,检索最相似的用户:
curl -sS -u elastic:123456 -X GET "http://localhost:9200/user/_search" \
-H "Content-Type: application/json" \
-d '{
"knn": {
"field": "interest_vector",
"query_vector": [0.6, 0.8, 0.0, 0.0],
"k": 3,
"num_candidates": 10
},
"_source": ["name"]
}'
其中:
query_vector是用户提供的查询向量,即搜索条件;k表示返回最相似的 k 个结果;num_candidates是 HNSW 内部候选集大小,建议设为 k 的 10~100 倍以保证召回率。
预期结果按相似度降序排列:Alice(相似度 1.0)、Charlie(0.7)、Bob(0.0)。
1.3.4 混合查询:向量 + 元数据过滤
实际应用中常需结合业务属性进行过滤。例如,仅检索名称以 “A” 开头且向量相似的用户:
curl -sS -u elastic:123456 -X GET "http://localhost:9200/user/_search" \
-H "Content-Type: application/json" \
-d '{
"knn": {
"field": "interest_vector",
"query_vector": [0.6, 0.8, 0.0, 0.0],
"k": 3,
"num_candidates": 10,
"filter": {
"wildcard": { "name": "A*" }
}
},
"_source": ["name"]
}'
这种“向量 + 结构化字段”的混合检索能力,使 Elasticsearch 在 RAG(检索增强生成)、个性化推荐等场景中极具优势。
1.4 总结
Elasticsearch 自 8.x 起已具备成熟的向量数据库能力:
- 支持
dense_vector类型及 HNSW 索引; - 提供高效的 ANN 搜索接口;
- 允许与全文检索、结构化查询无缝融合。
对于已有 ES 技术栈的团队,无需引入新系统即可实现语义搜索。若应用场景以纯向量检索为主且规模极大(如十亿级),可考虑 Milvus、Qdrant 等专用向量数据库;若需统一处理文本、向量与元数据,Elasticsearch 是一个务实而强大的选择。
在实际部署中,请注意:
- 写入前对向量进行 L2 归一化(若使用余弦相似度);
- 根据数据规模调整 HNSW 参数(如
ef_construction,m); - 监控内存与查询延迟,合理设置
num_candidates。
通过上述步骤,你已掌握构建端到端向量检索系统的核心方法。下一步可接入真实嵌入模型(如 Sentence-BERT),将文本或图像转化为向量,开启语义搜索之旅。
