目录
四.Elasticsearch数据库增加数据与索引&KNN查询数据
一.什么是向量数据库
向量数据库 是一种专门用于存储、索引和检索高维向量数据的数据库系统。与传统关系型数据库不同,向量数据库的核心能力是相似 度搜索 ,即根据向量之间的相似度快速找到最相似的数据。
核心概念 :
- 向量(Vector):由多个数值组成的数组,通常用于表示文本、图像、音频等非结构化数据的语义特征。
- 嵌入(Embedding):将非结构化数据转换为向量的过程,通过大模型的Embedding模型实现。
- 相似度计算:通过余弦相似度、欧氏距离、点积等方法衡量向量之间的相似程度。
- 近似最近邻搜索(ANN):在百万甚至亿级向量中快速找到最相似的Top-K结果。

数据在数据库中以向量的形式存储,检索时,也先将检索的关键字转化为向量,再去数据库里检索,这语义的概念学过深度学习的人很好理解。
应用场景 :
- RAG系统中的文档检索
- 推荐系统中的相似物品查找
- 图像搜索与识别
- 语义搜索
- 异常检测
为什么需要向量数据库
传统数据库的局限性 :
- 关系型数据库无法高效处理高维向量数据
- 精确匹配无法满足语义相似度搜索需求
- 传统索引结构(B+树、哈希表)不适合向量检索
向量数据库的优势:
高效相似度搜索 :通过专门的索引算法( HNSW 、 IVF 等)实现毫秒级检索
大规模支持 :可处理百万到亿级向量数据
语义理解 :支持基于语义而非关键词的检索
多模态支持 :可处理文本、图像、音频等多种数据类型
谁在使用向量数据库?
典型用户群体 :
AI 应用开发者 :构建 RAG 系统、智能问答、推荐系统
数据科学家 :进行相似度分析、聚类、异常检测
企业技术团队 :构建企业知识库、文档检索系统
产品经理 :需要语义搜索、个性化推荐等功能的业务场景
如何使用 向量数据库
基本工作流程:
- 数据准备:将原始数据(文档、图片等)通过Embedding模型转换为向量
- 向量存储:将向量数据导入向量数据库并建立索引
- 查询处理:将用户查询转换为向量,在数据库中搜索相似向量
- 结果返回:返回最相似的Top-K结果及其原始数据
选型关键因素 :
性能 :查询速度、吞吐量、延迟
成本 :开源免费 vs 云服务付费、硬件资源需求
易用性 : API 友好度、部署复杂度、文档完善度
扩展性 :单机 vs 分布式、水平扩展能力
功能特性 :是否支持混合检索、元数据过滤、实时更新
主流向量数据库产品概览
- FAISS:Facebook开源,本地部署,适合研究和小规模应用
- Milvus:开源分布式,企业级,适合大规模生产环境
- Elasticsearch:支持向量检索+全文检索混合,企业级首选
- Pinecone:云服务,开箱即用,适合快速原型
- Chroma:轻量级,易部署,适合中小型项目
- Qdrant:高性能,Rust实现,适合对性能要求高的场景
- Weaviate:图数据库+向量检索,适合复杂关系场景
向量数据库的本质 :专门用于高维向量相似度搜索的数据库系统,是RAG 系统的核心基础设施。
选型三要素 :
-
性能:
吞吐量: QPS,每秒请求数,系统能扛多大并发压力
延迟: P99, 99% 的用户在指定毫秒内看到结果,响应速度
召回率: Recall@K, 相关结果被找出来的比例 -
成本:开源 vs 云服务、资源消耗(单位 QPS 或每百万向量的资源消耗(CPU/GPU/内存))
-
易用性:部署复杂度、API友好度、团队熟悉度
主流产品定位 : -
FAISS:研究开发、小规模、本地部署
-
Milvus:企业级、大规模、分布式
-
Elasticsearch:混合检索、企业级、统一平台 ⭐⭐
-
Pinecone:快速原型、云服务、零运维
-
Chroma/Qdrant/Weaviate:轻量级、特定场景、快速集成
数据库的选择
场景 1 :个人学习 / 小项目
推荐 : FAISS 或 Chroma
理由:免费、简单、资源消耗低
场景 2 :企业生产环境(大规模)
推荐 : Milvus 或 Elasticsearch
理由:稳定性、扩展性、企业级功能
场景 3 :快速原型 / 无运维
推荐 : Pinecone
理由:开箱即用、零运维
场景 4 :混合检索需求
推荐 : Elasticsearch
理由:全文检索 + 向量检索统一平台
场景 5 :性能极致要求
推荐 : Qdrant 或 FAISS ( GPU )
理由:性能优秀、延迟低
二.Elasticsearch数据库初识
Elasticsearch ( ES ) 是一个分布式搜索与分析引擎。自 7.3 版本 起 , ES 原生支持 kNN (近邻)向量检索 ,实现了 全文检索 与 向量 语义检索 的统一,为混合搜索场景提供了强大支持。
为什么选择 Elasticsearch
适用场景
- 混合检索需求:需同时支持关键词匹配与语义相似度搜索。
- 已有ES基础设施:团队已熟悉 ES,可快速集成向量能力,降低迁移与学习成本。
- 企业级系统要求:需要成熟的监控、日志管理、安全控制和高可用架构。
- 多维复杂查询:需结合结构化字段、地理位置、时间范围与向量进行联合查询。
核心优势
✅ 统一平台 :全文检索 + 向量检索一体化,避免多系统维护。
✅ 企业就绪 :内置安全、监控、告警、可视化( Kibana )等运维能力。
✅ 灵活查询 DSL :支持复杂组合查询、过滤、聚合与排序。
✅ 横向扩展 :支持 PB 级数据规模,具备良好的集群伸缩性。
局限性
⚠️ 性能 vs 专用向量库 :在纯向量检索场景下,吞吐与延迟略逊于 FAISS 、 Milvus 等专业向量数据库。
⚠️ 资源开销较大 :向量索引会显著增加内存与存储消耗。
⚠️ 配置复杂度高 :需合理调优分片、索引策略及硬件资源配置。
谁适合使用 Elasticsearch 的向量检索?
- 已有 Elasticsearch 技术栈的团队
- 需要 关键词**+**语义混合搜索的产品(如智能客服、商品推荐、内容搜索)
- 对 系统可观测性、安全性、稳定性有较高要求的企业用户
- 业务涉及 多条件联合检索(如"价格 < 100 元 + 地理位置附近 + 语义相关")
性能表现

kNN 搜索类型对比

提示
- script_score 是 Elasticsearch 查询DSL(Domain Specific Language)中的一种自****定义打分机制。它允许通过编写 Painless****脚本(Elasticsearch 的安全脚本语言)来动态计算每个文档的相关性得分( _score )
- knn 查询是"新标准":利用向量索引实现高效近似搜索,快且易用,是生产首选。
- Elasticsearch 默认使用 HNSW**(Hierarchical Navigable Small World)**算法构建近似向量索引。
最佳实践
✅ 优先使用近似 kNN ( knn 查询) ,避免 script_score 全量计算。
✅ 合理设计分片数量 :过多分片增加协调开销,过少影响并行能力。
✅ 采用 RRF 融合策略 ,平衡关键词与语义信号,提升整体相关性。
✅ 定期优化索引 :合并段( force merge )、清理冷数据、监控 JVM 与磁盘使用。
✅ 控制向量维度 :高维向量( >1024 )显著增加内存与计算负担,建议降维或裁剪。
提示
从 Elasticsearch 8.0 起 ,向量字段类型为 dense_vector ,并持续增强对混合搜索、过滤向量检索(filtered kNN )的支持。建议使用 8.8+ 版本 以获得最佳功能与性能体验。
三.Elasticsearch数据库安装
官网: https://www.elastic.co/cn/elasticsearch
方法 1 :使用 Docker (最简单,推荐开发测试)
方法 2 :本地安装( Linux/macOS/Windows )
官网下载: Download Elasticsearch | Elastic (可以选择下载压缩包本地安装 or 通过Docker使用)
解压并启动
本文使用Docker的方式来安装,对于企业来说,会选择安装多节点的版本,但对于学习者来说,单节点足矣,
在docker hub上搜索elastic ,然后找到elasticsearch,然后可以选择到相关的版本,执行相关命令,
下载docker desktop,检查电脑是否安装WSL(科学上网安装起来比较快),新版要求有WSL,要不然用不了,
拉取官方镜像(以 9.4.6 为例)
docker pull elastic/elasticsearch:9.4.6
启动容器(单节点,关闭安全认证便于测试)第一个-p代表的是HTTP端口号,第二个-p代表的是多节点之间通信的端口号,如果是单节点的话,就把第二个-p和端口号删除,最后的东西是你拉下来的镜像的名称
docker run -d --name es-knn -p 9200:9200 -p 9300:9300 -e discovery.type=single-node -e xpack.security.enabled=false elastic/elasticsearch:9.4.6
执行之后,docker desktop会出现相应的容器,找到es的那个容器,点击按钮启动
Python 的使用
安装与连接 :
安装 Elasticsearch 客户端
pip install elasticsearch
四.Elasticsearch数据库增加数据与索引&KNN查询数据
完整代码示例 :
elasticsearch的参考文档在官网Docs的 Reference里。也去可以去pypi网站上查找相关参考文档(搜索elasticsearch)
python
from elasticsearch import Elasticsearch
import numpy as np
# 准备数据
items = [
{"type": "phone", "id": "用户A", "number": 13800001234},
{"type": "phone", "id": "用户B", "number": 13800005678},
{"type": "order", "id": "订单1001", "number": 203011010001},
{"type": "order", "id": "订单1002", "number": 203011010123},
{"type": "order", "id": "订单2001", "number": 203012150045},
{"type": "phone", "id": "用户C", "number": 13912345678},
{"type": "phone", "id": "用户D", "number": 13798765432},
{"type": "order", "id": "订单3001", "number": 205001020333},
{"type": "order", "id": "订单3002", "number": 205001020777},
{"type": "phone", "id": "用户E", "number": 13622223333},
]
# 链接ES
from numpy import indices
es = Elasticsearch(
# scheme="http",为设置链接协议为http协议
hosts = [{'host':'localhost','port':9200,'scheme':'http'}],
#超过30秒没有响应就会触发超时异常
request_timeout = 30 # 默认0秒会触发超时
)
# 创建索引参数
index_name = 'number_vector'
mapping = {
#mappings为索引的映射配置,定义了索引的字段和字段的类型
#mappings和properties是固定的,再里面的才是自己写的
"mappings": {
"properties": {
# 四个属性,其中一个是索引,另外三个为普通属性
"number_vector": {
"type": "dense_vector",# 密集向量类型
"dims": 1, #维度为1
"index": True, #是否为索引
"similarity": "l2_norm" # L2欧式距离
},
"type":{"type":"keyword"}, #es只有keyword和text两种字符串类型,keyword是精确匹配,text是分词匹配
"id":{"type":"keyword"},
"number":{"type":"long"} #长整型
}
}
}
# 删除已存在的索引
if es.indices.exists(index=index_name): # 索引存在
es.indices.delete(index=index_name)
# 创建索引
try:
#新写法
es.indices.create(index=index_name, mappings=mapping['mappings'])
except TypeError:
#马上淘汰的旧写法,不建议使用
es.indices.create(index=index_name, body=mapping)
# 构建数据插入数据库
dimension = 1
for i ,item in enumerate(items):
# 把number字段转换为浮点数,并放入一个列表中,作为索引字段(有大模型时可以转化为语义向量再当作索引字段)
number_vector = [float(item['number'])]
# 在索引中创建或更新数据
es.index(
index = index_name,
id = i+1,
document= {
# 索引
"number_vector": number_vector,
#下面皆为属性字段
"type": item['type'],
"id": item['id'],
"number": item['number']
}
)
print(f'已向索引添加 {len(items)} 个数字向量 (维度={dimension})')
# 刷新索引
es.indices.refresh(index=index_name)
query_number = 205001020500
#转换成和数据库里存储同样的形式
query_vector = [float(query_number)]
k = 5 # 搜索最接近的5个向量
knn_query ={
'size':k,
'_source':['type','id','number','number_vector'], #搜索结果返回的字段
# knn参数
'knn':{
# 根据number_vector字段进行knn查询
'field':'number_vector',
# 查询向量
'query_vector':query_vector,
'k':k,
'num_candidates':100 # 粗排阶段候选向量数量(100个),然后再从100个中选出5个,越大精度消耗的资源越多
}
}
results = es.search(index=index_name,body=knn_query)
print("\n查询数字:", query_number)
print(f"Top-{k} 最相近数字(按 L2 距离,越小越相近):")
print(results)
for rank, hit in enumerate(results["hits"]["hits"], start=1):
source = hit['_source']
print(f"{rank}. 得分={hit['_score']:.4f} | 类型={source['type']} | 标识={source['id']} | 数字={source['number']}")