ElasticSearch文档读写操作入门
ElasticSearch 基础概念
在深入探讨 ElasticSearch 的文档读写操作之前,我们先来理解一些关键的基础概念。
索引(Index)
索引是 ElasticSearch 存储数据的逻辑容器,类似于关系型数据库中的数据库概念。一个索引可以包含多个类型(在 ElasticSearch 7.0 之后,类型的概念逐渐被弱化,单个索引建议只存储一种类型的数据),并且每个索引都有自己的配置,例如分片数量、副本数量等。可以将索引看作是一个存放相似文档的地方,比如可以创建一个名为 “products” 的索引来存放所有产品相关的文档。
文档(Document)
文档是 ElasticSearch 中最基本的数据单元,它是一个 JSON 格式的对象,包含了各种字段及其对应的值。每个文档都存在于一个索引中,并且可以通过唯一的标识符(ID)来进行区分。例如,一个产品文档可能包含产品名称、价格、描述等字段,如下所示:
{
"product_name": "示例手机",
"price": 2999,
"description": "一款高性能的智能手机"
}
分片(Shard)
为了处理大规模的数据,ElasticSearch 将索引划分成多个分片。每个分片本身就是一个完整的搜索引擎,它可以独立地进行数据的存储和检索。ElasticSearch 会自动管理这些分片,在节点之间进行负载均衡。例如,一个大的 “products” 索引可以被分成多个分片,分布在不同的服务器节点上,这样可以提高数据处理的并行性和整体性能。
副本(Replica)
副本是分片的拷贝,主要用于提高数据的可用性和查询性能。当某个分片所在的节点出现故障时,副本可以替代它继续提供服务。同时,副本也可以用于分担读请求,提高查询的响应速度。每个分片可以有多个副本,ElasticSearch 会自动维护副本与主分片之间的数据一致性。
安装与环境准备
在开始进行 ElasticSearch 的文档读写操作之前,需要先安装 ElasticSearch 并确保环境配置正确。
安装 ElasticSearch
- 下载:从 ElasticSearch 的官方网站(https://www.elastic.co/downloads/elasticsearch)下载适合你操作系统的安装包。
- 解压:对于压缩包形式的安装包,解压到你希望安装的目录。例如,在 Linux 系统下,可以使用以下命令解压:
tar -zxvf elasticsearch-<version>.tar.gz
- 启动:进入解压后的目录,在 bin 目录下执行启动脚本。在 Linux 或 macOS 系统下:
cd elasticsearch-<version>/bin
./elasticsearch
在 Windows 系统下,打开命令提示符,进入 bin 目录,执行 elasticsearch.bat
。
安装 Elasticsearch 客户端
为了方便与 ElasticSearch 进行交互,我们可以安装 Elasticsearch 客户端。这里以 Python 的 Elasticsearch 客户端为例,使用 pip
进行安装:
pip install elasticsearch
文档写入操作
创建文档
在 ElasticSearch 中创建文档,需要指定索引、文档类型(在新版中可不指定)和文档内容。下面以 Python 的 Elasticsearch 客户端为例,展示如何创建一个文档。
首先,导入 Elasticsearch 客户端库并建立连接:
from elasticsearch import Elasticsearch
es = Elasticsearch([{'host': 'localhost', 'port': 9200}])
然后,创建一个文档,例如创建一个名为 “test_index” 的索引下的产品文档:
doc = {
"product_name": "示例笔记本电脑",
"price": 5999,
"description": "轻薄便携的高性能笔记本"
}
response = es.index(index='test_index', body=doc)
print(response)
在上述代码中,es.index
方法用于创建文档。index
参数指定索引名称,body
参数为文档的具体内容。执行代码后,ElasticSearch 会返回一个响应,包含文档的创建结果,例如创建的文档 ID、版本等信息。
更新文档
如果文档已经存在,我们可以对其进行更新。ElasticSearch 提供了多种更新文档的方式。
- 全量更新:可以直接使用
index
方法再次写入文档,ElasticSearch 会根据文档 ID 进行判断,如果文档存在则更新,不存在则创建。例如:
updated_doc = {
"product_name": "更新后的示例笔记本电脑",
"price": 6299,
"description": "升级配置后的轻薄高性能笔记本"
}
response = es.index(index='test_index', id=response['_id'], body=updated_doc)
print(response)
这里通过 id
参数指定要更新的文档 ID,将新的文档内容传递给 body
参数。
- 部分更新:使用
update
方法可以对文档的部分字段进行更新。例如,只更新价格字段:
update_body = {
"doc": {
"price": 6499
}
}
response = es.update(index='test_index', id=response['_id'], body=update_body)
print(response)
在 update_body
中,doc
字段包含了要更新的具体内容。
批量写入文档
当需要写入大量文档时,逐个写入效率较低,此时可以使用批量写入操作。ElasticSearch 提供了 bulk
方法来实现批量操作。
假设我们有一个包含多个产品文档的列表:
products = [
{
"product_name": "示例平板电脑",
"price": 1999,
"description": "小巧便携的平板电脑"
},
{
"product_name": "示例耳机",
"price": 299,
"description": "高音质无线耳机"
}
]
actions = []
for product in products:
action = {
"index": {
"_index": "test_index"
}
}
actions.append(action)
actions.append(product)
response = es.bulk(body=actions)
print(response)
在上述代码中,我们构建了一个包含多个操作的列表 actions
,每个操作由一个索引动作和对应的文档组成。然后使用 es.bulk
方法执行批量操作。
文档读取操作
根据 ID 获取文档
通过文档的唯一 ID 可以快速获取文档内容。以 Python 客户端为例:
response = es.get(index='test_index', id=response['_id'])
print(response['_source'])
这里 es.get
方法的 index
参数指定索引,id
参数指定文档 ID。响应中的 _source
字段包含了文档的原始内容。
搜索文档
ElasticSearch 强大的搜索功能允许我们根据各种条件搜索文档。
- 简单搜索:例如搜索价格大于 3000 的产品文档:
query = {
"query": {
"range": {
"price": {
"gt": 3000
}
}
}
}
response = es.search(index='test_index', body=query)
for hit in response['hits']['hits']:
print(hit['_source'])
在上述代码中,query
定义了搜索条件,range
表示范围查询,gt
表示大于。es.search
方法执行搜索,hits
字段包含了搜索结果,通过遍历 hits['hits']
可以获取每个匹配的文档。
- 全文搜索:ElasticSearch 对文本字段的全文搜索非常强大。例如搜索描述中包含 “高性能” 的产品文档:
query = {
"query": {
"match": {
"description": "高性能"
}
}
}
response = es.search(index='test_index', body=query)
for hit in response['hits']['hits']:
print(hit['_source'])
match
查询用于对文本字段进行全文搜索,ElasticSearch 会对文本进行分词处理,然后查找匹配的文档。
- 组合查询:可以将多个查询条件组合起来,实现更复杂的搜索。例如搜索价格大于 3000 且描述中包含 “高性能” 的产品文档:
query = {
"query": {
"bool": {
"must": [
{
"range": {
"price": {
"gt": 3000
}
}
},
{
"match": {
"description": "高性能"
}
}
]
}
}
}
response = es.search(index='test_index', body=query)
for hit in response['hits']['hits']:
print(hit['_source'])
bool
查询可以组合多个查询条件,must
表示所有条件都必须满足。
分页查询
当搜索结果较多时,需要进行分页显示。ElasticSearch 通过 from
和 size
参数来实现分页。例如,每页显示 10 条记录,获取第二页的结果:
query = {
"query": {
"match_all": {}
}
}
from_value = 10
size_value = 10
response = es.search(index='test_index', body=query, from_=from_value, size=size_value)
for hit in response['hits']['hits']:
print(hit['_source'])
from_
参数指定从结果集的第几条记录开始返回(从 0 开始计数),size
参数指定每页返回的记录数。
文档删除操作
根据 ID 删除文档
通过文档 ID 可以删除指定的文档。以 Python 客户端为例:
response = es.delete(index='test_index', id=response['_id'])
print(response)
es.delete
方法的 index
参数指定索引,id
参数指定要删除的文档 ID。执行后 ElasticSearch 会返回删除操作的结果。
条件删除文档
ElasticSearch 本身并没有直接提供条件删除的 API,但可以通过先搜索出符合条件的文档 ID,然后逐个删除来实现类似功能。例如,删除价格小于 1000 的产品文档:
query = {
"query": {
"range": {
"price": {
"lt": 1000
}
}
}
}
response = es.search(index='test_index', body=query)
for hit in response['hits']['hits']:
es.delete(index='test_index', id=hit['_id'])
首先通过搜索获取符合条件的文档,然后遍历结果集,使用 es.delete
方法逐个删除文档。
深入理解文档读写原理
写入原理
- 索引与分片:当我们写入一个文档时,ElasticSearch 首先根据文档的 ID(如果没有指定,ElasticSearch 会自动生成一个)通过哈希算法计算出该文档应该存储在哪个分片上。然后,主分片会接收这个文档并进行写入操作。
- 副本同步:主分片写入成功后,会将数据同步到它的副本分片上。只有当所有副本分片都成功同步数据后,写入操作才会被认为是成功的(这可以通过
consistency
参数进行调整,例如one
表示只要有一个副本成功同步即可,all
表示所有副本都要成功同步)。
读取原理
- 搜索请求分发:当发起一个搜索请求时,ElasticSearch 会将请求发送到索引的所有分片(包括主分片和副本分片)上。每个分片独立执行搜索操作,并返回部分结果。
- 结果合并:ElasticSearch 协调节点会收集所有分片返回的结果,并根据相关性得分等因素进行合并和排序,最终返回给客户端完整的搜索结果。
性能优化与注意事项
性能优化
- 合理设置分片和副本数量:分片数量过多会增加管理开销,过少则可能导致数据存储和查询性能瓶颈。副本数量过多会占用更多的存储空间和网络带宽,应根据实际的读/写负载进行合理设置。
- 使用批量操作:如前面提到的批量写入和批量读取操作,可以减少网络交互次数,提高整体性能。
- 优化查询语句:避免使用过于复杂或低效的查询,例如尽量避免使用通配符查询(
wildcard
),因为它的性能开销较大。
注意事项
- 版本控制:ElasticSearch 对文档进行更新操作时,会增加文档的版本号。在并发操作时,需要注意版本冲突问题,可以通过在更新请求中指定版本号来避免覆盖最新的数据。
- 数据一致性:由于副本同步存在一定的延迟,在某些对数据一致性要求极高的场景下,可能需要等待副本同步完成后再进行读取操作,可以通过设置
refresh
参数来实现。
通过以上内容,我们全面地了解了 ElasticSearch 的文档读写操作,包括基础概念、安装配置、具体的读写操作、原理以及性能优化等方面。希望这些知识能帮助你在实际项目中更好地使用 ElasticSearch 进行数据的管理和检索。