Elasticsearch是一个基于Lucene构建的开源搜索引擎,广泛应用于全文搜索、日志分析和实时数据分析等领域。它允许你快速地存储、搜索和分析大规模数据,特别适合于需要复杂搜索功能的应用程序。Elasticsearch的分布式特性使得它可以扩展至数百台服务器,处理PB级数据。
基础概念
文档和词条的定义
- 文档(Document):文档是Elasticsearch中的基本数据单元,类似于关系数据库中的行。每个文档是一个JSON对象,包含了一系列字段及其值。例如,一个网页或一个商品信息都可以是一个文档。
- 词条(Term):词条是经过分词处理后的基本单位,通常是单词或短语。在Elasticsearch中,文档的内容会被分析器(Analyzer)处理,分解成一系列词条。例如,文本“我是中国人”可能会被分解为“我”、“是”、“中国人”、“中国”和“国人”。
倒排索引的组成:词典和倒排列表
- 词典(Term Dictionary):词典包含了所有文档中的词条,是倒排索引的核心部分。词典中的每个词条都指向包含该词条的文档列表,即倒排列表。
- 倒排列表(Inverted List):倒排列表记录了包含某个词条的所有文档的ID,以及词条在每个文档中的位置信息。它可能还包括词频(即这个词在文档中出现的次数)和偏移量(记录词项在文档中的起始位置)。
分词器
分词器的重要性
在Elasticsearch中,分词器(Analyzer)是处理文本数据的核心组件。它的主要作用是将文本字符串分解成一系列的词项(Tokens),这些词项随后被用于创建倒排索引。分词器的选择和配置对搜索的准确性和性能有着直接的影响。
为什么分词器重要?
- 提高搜索相关性:正确的分词可以确保文档中的关键词被准确地索引,从而提高搜索结果的相关性。
- 支持多语言处理:不同的语言有不同的分词规则。例如,中文文本需要特定的分词器来正确处理。
- 优化索引性能:有效的分词可以减少索引中的词项数量,从而提高索引和搜索的性能。
- 增强用户体验:准确的分词能确保用户在搜索时得到更精确的结果,提升用户体验。
IK分词器的使用和模式
IK分词器是一个专门为中文文本设计的分词器,它能够更准确地处理中文文本。
安装IK分词器
在Elasticsearch中安装IK分词器通常涉及到下载相应的插件并放置到Elasticsearch的插件目录中。
使用IK分词器
IK分词器提供了两种模式:ik_smart
和ik_max_word
。
- ik_smart模式:
- 这是一种粗粒度的分词模式,它能够快速地处理文本,并且尽可能少地产生词项。
- 适用于快速搜索和大多数场景,因为它可以减少索引的大小并提高搜索性能。
- ik_max_word模式:
- 这是一种细粒度的分词模式,它会尽可能多地产生词项,包括可能的组合和派生词。
- 适用于需要高精度搜索的场景,例如搜索引擎的索引阶段。
配置IK分词器
IK分词器允许自定义词典,这意味着您可以添加、修改或删除特定的词项,以满足特定的需求。
- 添加自定义词:
- 通过编辑IK分词器配置文件,您可以添加新的词项到词典中。
- 这对于处理专有名词、新词或特定领域的词汇特别有用。
- 停用词过滤:
- IK分词器还支持停用词过滤,允许您指定一些不希望被索引的词项,如常见的助词、介词等。
索引库操作
索引库
- 定义:索引库(Index)是Elasticsearch中的一个逻辑存储单元,类似于数据库中的表。每个索引库可以包含多个文档。
- 用途:索引库用于存储和组织文档,每个索引库都有自己独特的数据结构和配置。
- 属性:
- 名称:每个索引库都有一个唯一的名称。
- 类型:指定了文档中字段的类型,如文本、数值、日期等。
- 映射(Mapping):定义了索引库中字段的类型和属性。
- 分片(Shards):将索引库分割成多个分片,以提高索引和搜索的性能。
- 副本(Replicas):为分片创建副本,以提高数据的可用性和查询性能。
Mapping
- 定义:Mapping定义了索引库中字段的类型和属性,如是否可分词、是否索引等。
- 作用:Mapping用于约束索引库中的文档结构,确保数据的准确性和一致性。
- 属性:
- type:字段的数据类型,如text、keyword、integer等。
- index:决定字段是否可以被搜索。
- analyzer:指定用于分词的字段分析器。
- properties:字段的子字段映射。
创建索引库
- 定义Mapping:根据要存储的数据定义Mapping,包括字段类型、分词器等。
- 创建索引:使用Elasticsearch的API或命令行工具创建索引库,并指定Mapping。
- 验证索引:确认索引库已成功创建,可以使用Elasticsearch的Rest API查询索引信息。
修改索引库
- 添加新字段:可以在已存在的索引库中添加新的字段,不会影响已有的数据和倒排索引。
- 更新Mapping:在某些情况下,如果需要更改字段的类型或属性,可能需要重新创建索引库。
- 数据迁移:如果需要大规模地更改索引库的结构,可能需要进行数据迁移,这通常涉及到复制数据到新的索引库中。
注意事项
索引不可变性:一旦创建,索引库的结构(Mapping)通常不可更改。如果需要修改,可能需要创建一个新的索引库。
文档操作
文档的创建、查询、修改和删除
创建文档
- 创建文档是指向Elasticsearch索引中添加新的数据。
- 使用
POST /{索引库名}/_doc/{文档ID}
API,其中{文档ID}
是文档的唯一标识。 - 发送一个JSON格式的请求体,包含文档的数据。
查询文档
- 查询文档是指从Elasticsearch索引中检索特定的数据。
- 使用
GET /{索引库名}/_doc/{文档ID}
API,其中{文档ID}
是文档的唯一标识。 - 返回请求的文档,如果文档存在。
修改文档
- 修改文档包括全量修改和局部修改两种方式。
- 全量修改:使用
PUT /{索引库名}/_doc/{文档ID}
API,发送一个包含完整文档数据的JSON请求体。 - 局部修改:使用
POST /{索引库名}/_update/{文档ID}
API,发送一个包含要修改的字段的JSON请求体。
删除文档
- 删除文档是指从Elasticsearch索引中移除一个文档。
- 使用
DELETE /{索引库名}/_doc/{文档ID}
API,其中{文档ID}
是文档的唯一标识。
全量修改与局部修改的区别
全量修改
- 适用于文档内容完全改变的情况。
- 本质上是先删除旧文档,然后创建一个新文档。
- 操作过程中,旧文档会被立即从索引中移除,新文档会立即被索引。
局部修改
- 适用于只修改文档的部分字段。
- 操作过程中,旧文档不会被立即删除,而是被标记为已修改。
- 新数据会应用到文档上,但旧文档仍然存在于索引中,直到被合并或删除。
示例代码
创建文档
POST /my_index/_doc/1
{
"name": "张三",
"age": 30,
"email": "zhangsan@example.com"
}
查询文档
GET /my_index/_doc/1
全量修改文档
PUT /my_index/_doc/1
{
"name": "李四",
"age": 25,
"email": "lisi@example.com"
}
局部修改文档
POST /my_index/_update/1
{
"doc": {
"email": "zhangsan_new@example.com"
}
}
删除文档
DELETE /my_index/_doc/1
高级用法
DSL查询
- 全文检索查询:用于处理全文搜索,如
match
和multi_match
。match
:匹配特定字段中的文本。multi_match
:在多个字段中搜索文本。
- 精确查询:用于精确匹配,如
term
和range
。term
:查找特定字段中的确切词项。range
:根据数值或日期范围过滤文档。
- 地理坐标查询:用于地理位置搜索,如
geo_bounding_box
和geo_distance
。geo_bounding_box
:按矩形区域过滤。geo_distance
:按距离过滤。
- 布尔查询:组合多个查询条件,如
must
、should
、must_not
。must
:所有条件必须满足。should
:至少满足一个条件。must_not
:所有条件都不满足。
示例
查看代码
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Elasticsearch" } }
],
"should": [
{ "term": { "category": "search" } },
{ "term": { "category": "big data" } }
],
"must_not": [
{ "range": { "publish_date": { "lt": "2020-01-01" } } }
]
}
}
}
排序和分页
排序:使用
sort
字段,可按关键词、数值、日期等字段排序。json"sort": [ { "publish_date": { "order": "desc" } }, { "price": { "order": "asc" } } ]
分页:通过
from
和size
参数控制。json"from": 0, "size": 10
高亮显示搜索结果
在查询中包含highlight
字段,指定高亮字段和标签。
"highlight": {
"fields": {
"title": {}
},
"pre_tags": ["<em>"],
"post_tags": ["</em>"]
}
数据聚合
- 桶聚合:用于分组,如
term
和date_histogram
。term
:按字段值分组。date_histogram
:按日期分组。
- 度量聚合:用于计算,如
avg
、max
、min
。avg
:计算平均值。max
:找出最大值。min
:找出最小值。
示例
"aggs": {
"category_terms": {
"terms": { "field": "category" }
},
"price_stats": {
"stats": { "field": "price" }
}
}
索引文档的过程
文档解析
解析JSON文档:Elasticsearch接收JSON格式的文档,并解析其内容,提取出字段和值。
字段映射
应用Mapping:根据索引的映射(Mapping)定义,Elasticsearch处理文档中的字段,确定如何索引和存储这些字段。例如,文本字段可能被映射为全文搜索字段,日期字段可能被映射为日期类型字段。
文本分析
分词和过滤:对于文本字段,Elasticsearch使用分析器(Analyzer)进行处理。分析器包括字符过滤器、分词器和词汇单元过滤器,将文本分解为单独的词项,去除不必要的字符,并可能将词项转换为词干形式。
创建倒排索引
构建词典和倒排列表:分析后的词项用于创建或更新倒排索引。每个词项都会指向包含它的文档列表,记录词项在每个文档中的位置和频率。
文档存储
正排索引(Doc Values):除了倒排索引,Elasticsearch还会将文档的原始数据存储在正排索引中,允许快速访问文档的字段值,这对于排序和聚合操作非常重要。
写入事务日志
Translog记录:为了确保数据的持久性和一致性,Elasticsearch会将文档的变更写入事务日志。事务日志是一个在磁盘上的日志文件,用于记录所有的写操作。
刷新操作
近实时搜索:默认情况下,Elasticsearch每隔一定时间(例如1秒)会执行一次刷新操作。刷新操作会将内存中的数据写入到文件系统缓存中,此时数据就可以被搜索到了。
持久化操作
数据写入磁盘:当事务日志达到一定大小或者经过一定时间后,Elasticsearch会执行一次持久化操作。持久化操作会将内存中的数据写入到磁盘上的segments中,并清空事务日志。
合并操作
优化索引性能:随着时间的推移,索引中会生成大量的segments。为了优化搜索性能和减少磁盘空间的使用,Elasticsearch会定期执行合并操作,将多个segments合并为一个更大的segment。
更新和删除文档的过程
Elasticsearch中的文档更新和删除操作涉及到对倒排索引和正排索引的修改,以及事务日志的更新。这些操作确保了数据的一致性和持久性。
更新文档
更新文档在Elasticsearch中是一个复合操作,实质上是删除旧文档后索引新文档的过程。
- 检索文档:首先,Elasticsearch会检索要更新的文档,通常涉及到查询倒排索引以找到文档的ID。
- 版本检查(如果启用):如果索引启用了版本控制,Elasticsearch会检查检索到的文档的版本号与请求中的版本号是否一致。
- 写入事务日志:更新操作会被写入事务日志,以确保操作的持久性。
- 删除旧文档:Elasticsearch会将旧版本的文档从倒排索引中删除,更新倒排列表,移除指向旧文档的引用。
- 索引新文档:接下来,Elasticsearch会将新版本的文档索引到倒排索引中,包括对新文档进行文本分析、创建新的倒排索引条目,并更新正排索引。
- 刷新和持久化:一旦新文档被索引,Elasticsearch可能会执行刷新操作,使新文档可搜索,并随后执行持久化操作,将事务日志中的操作写入磁盘。
- 返回结果:最后,Elasticsearch返回更新操作的结果,包括文档的ID、版本号和操作类型。
删除文档
删除文档的过程相对简单,但同样重要。
- 检索文档:首先,Elasticsearch会检索要删除的文档。
- 版本检查(如果启用):如果索引启用了版本控制,Elasticsearch会检查检索到的文档的版本号与请求中的版本号是否一致。
- 写入事务日志:删除操作会被写入事务日志。
- 从倒排索引中删除文档:Elasticsearch会从倒排索引中移除文档的所有词项,并更新倒排列表,移除指向该文档的引用。
- 更新正排索引:Elasticsearch会从正排索引中移除文档的数据。
- 刷新和持久化:删除操作完成后,Elasticsearch可能会执行刷新操作,以使文档从搜索结果中消失,并随后执行持久化操作。
- 返回结果:最后,Elasticsearch返回删除操作的结果,包括文档的ID、版本号和操作类型。
文档不可变性和标记删除
在Elasticsearch中,文档实际上是不可变的。当一个文档被索引后,它就不能被修改或删除。相反,Elasticsearch使用了一种标记删除的方法来处理删除请求。在.del
文件中为该文档添加一个删除标记,尽管文档在物理上仍然存在于磁盘上的segment中,但它已经被逻辑上删除了。在查询时,Elasticsearch会检查.del
文件来确定哪些文档应该被排除在搜索结果之外。在后台合并segments时,Elasticsearch会清除这些标记的数据。
保证读写一致
Elasticsearch在处理并发读写操作时,采用了多种机制来确保数据的一致性。这些机制包括乐观并发控制、版本号管理、事务日志、刷新和持久化操作、读取偏好设置、索引块以及分布式一致性保证。
乐观并发控制和版本号
Elasticsearch使用乐观并发控制(Optimistic Concurrency Control, OCC)来处理并发读写操作。它假设冲突不会经常发生,因此不会在操作前锁定资源。每当文档被更新时,Elasticsearch会检查请求中的版本号与文档当前版本号是否一致。如果版本号不匹配,操作会失败,并返回一个版本冲突错误。这种机制允许Elasticsearch快速处理并发操作,同时确保数据的一致性。
事务日志和刷新/持久化操作
Elasticsearch中的事务日志(Translog)记录了所有的写操作。在执行写操作时,操作首先被写入事务日志,然后才应用到倒排索引。这确保了即使在发生故障时,也能恢复未持久化的数据。此外,Elasticsearch定期执行刷新操作,将内存中的数据写入文件系统缓存,使数据可搜索。持久化操作则会将内存中的数据写入磁盘,并清空事务日志。这些操作的频率可以通过配置进行调整,以平衡性能和数据安全性。
读取偏好设置和索引块
Elasticsearch允许在查询时设置读取偏好。例如,可以使用_preference
参数指定一致性级别,以确保读取操作能够看到最新的写操作结果。索引块是一种机制,用于在执行某些操作(如批量索引或删除)时防止索引的并发修改。这可以确保在关键操作期间数据的一致性。
分布式一致性保证
在分布式环境中,Elasticsearch使用分布式一致性协议(如Zen Discovery)来确保节点之间的数据一致性。主节点负责处理集群状态的变化,并通过集群状态更新与数据节点同步。这确保了在分布式集群中,所有节点都能访问到最新的数据状态。