Elasticsearch 教程
Elasticsearch 是一个分布式搜索和分析引擎,其核心概念之一就是 索引。索引的设计、创建、管理、优化是使用 Elasticsearch 进行高效数据存储和检索的关键。为了更全面和深入地理解 Elasticsearch 索引,我们将从基础概念到高级功能,逐步讲解索引的方方面面。
一、Elasticsearch 索引基础概念
1. 什么是索引?
在 Elasticsearch 中,索引(Index)是文档的集合,类似于关系型数据库中的“表”。每个索引都有唯一的名字,用于标识和操作这个索引中的数据。
索引的核心概念:
- 文档(Document):Elasticsearch 中的数据单元,类似于数据库中的行。每个文档是 JSON 格式。
- 字段(Field):文档的属性或列,每个字段都可以有不同的数据类型,如字符串、整数、日期等。
- 分片(Shard):Elasticsearch 将每个索引划分为多个分片,分片是分布式存储和处理的最小单位。每个分片都是一个完整的 Lucene 索引。
- 副本(Replica):每个分片可以有多个副本,用于提高查询性能和容错能力。主分片用于数据写入,副本分片主要用于读取。
Elasticsearch 的特点:
- 分布式架构:索引可以分布在多个节点上,具有很强的扩展性。
- 全文搜索:支持复杂的查询语言和全文检索功能。
- 实时性:Elasticsearch 提供了接近实时(NRT)的搜索和索引功能。
2. 文档与字段类型
文档是 Elasticsearch 中存储的最小数据单元,字段则是文档中的属性。字段类型决定了如何索引和存储数据。
常见字段类型:
- text:用于存储可分词的文本,适合全文搜索。
- keyword:用于存储精确匹配的字符串,不会进行分词处理,适合排序和聚合操作。
- numeric:包括 integer
, float
, long
等,用于数值类型的数据。
- date:用于存储日期类型,可以指定格式。
- boolean:存储布尔值,只有 true
和 false
两个取值。
示例文档结构:
{
"title": "Elasticsearch 从入门到精通",
"author": "张三",
"published_date": "2024-09-20",
"pages": 350,
"tags": ["搜索引擎", "分布式", "Elasticsearch"]
}
二、索引的创建
1. 自动创建索引
Elasticsearch 可以在首次插入文档时自动创建索引。此时,Elasticsearch 会根据插入的数据自动推断字段类型,并创建相应的映射(mapping)。
POST /my_index/_doc
{
"title": "自动创建索引",
"author": "李四",
"published_date": "2024-09-19"
}
然而,自动创建索引的方式缺乏对字段类型的精确控制,可能会导致意外的字段映射。因此,建议在生产环境中手动定义索引。
2. 手动创建索引
手动创建索引时,我们可以通过 PUT
请求指定索引的设置和映射。典型的索引配置包括分片数量、副本数量、字段类型等。
示例:手动创建索引
PUT /books
{
"settings": {
"number_of_shards": 3, # 设置主分片数
"number_of_replicas": 1 # 设置副本数
},
"mappings": {
"properties": {
"title": {
"type": "text" # 用于全文检索
},
"author": {
"type": "keyword" # 精确匹配
},
"published_date": {
"type": "date" # 日期类型
},
"pages": {
"type": "integer" # 整数类型
},
"tags": {
"type": "keyword" # 适合做聚合分析
}
}
}
}
三、索引结构:设置与映射
1. Settings(索引设置)
索引设置定义了索引的核心配置,例如分片数、副本数、刷新频率等。这些设置影响了索引的性能、可扩展性和可靠性。
常见的索引设置
- number_of_shards:主分片数量,决定了数据的水平分割,默认是 1。分片数不能在索引创建后更改。
- number_of_replicas:每个主分片的副本数量,副本提高查询性能和容错能力。可以动态调整。
- refresh_interval:控制索引的刷新间隔,默认是 1 秒,刷新使得新数据可见。
示例:索引设置
PUT /my_index
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 2,
"refresh_interval": "30s" # 30 秒刷新一次
}
}
2. Mappings(映射)
映射决定了索引中每个字段的数据类型和行为。例如,哪些字段可以被全文搜索,哪些字段适合用于聚合分析。
字段属性控制:
- index:指定字段是否可以被索引,默认为
true
。如果设置为false
,该字段将无法被搜索。 - analyzer:指定分词器,用于
text
类型字段。 - doc_values:用于控制是否存储字段值以支持排序和聚合,默认对
keyword
和numeric
类型字段启用。
示例:映射定义
PUT /my_index
{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "standard" # 使用标准分词器
},
"status": {
"type": "keyword", # 适合精确匹配
"index": true
},
"views": {
"type": "integer", # 数值类型
"doc_values": true # 允许聚合和排序
},
"created_at": {
"type": "date" # 日期类型
}
}
}
}
3. 动态映射与手动映射
Elasticsearch 支持 动态映射,即自动推断字段类型并生成映射。然而,在生产环境中,建议使用手动映射,以确保字段类型符合预期,并且可以细粒度控制字段的索引行为。
四、索引的操作与管理
1. 插入文档
文档插入使用 POST
或 PUT
请求,数据格式为 JSON。如果未指定文档 ID,Elasticsearch 会自动生成一个唯一的 ID。
示例:插入文档
POST /my_index/_doc
{
"title": "Elasticsearch 指南",
"author": "王五",
"published_date": "2024-09-18",
"pages": 400
}
也可以通过 PUT
明确指定文档 ID:
PUT /my_index/_doc/1
{
"title": "Elasticsearch 深度探索",
"author": "赵六",
"published_date": "2024-09-20",
"pages": 500
}
2. 查询文档
可以通过文档 ID 查询单个文档:
GET /my_index/_doc/1
3. 更新文档
Elasticsearch 提供了部分更新文档的功能,使用 _update
API 可以修改文档的部分字段,而不必替换整个文档。
示例:更新文档
POST /my_index/_update/1
{
"doc": {
"pages": 600
}
}
4. 删除文档
使用 _delete
API 可以删除指定的文档:
DELETE /my_index/_doc/1
五、索引的查询与搜索
1. 简单查询
Elasticsearch 支持复杂的查询语言,查询可以通过 match
、term
等子句来构建。
示例:全文搜索
POST /my_index/_search
{
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
2. 过滤查询
通过 filter
子句,可以构建精确的过滤查询,适合对结构化数据(如 keyword
、date
)进行精确匹配。
示例:过滤查询
POST /my_index/_search
{
"query": {
"bool": {
"filter": {
"term": {
"author": "张三"
}
}
}
}
}
3. 聚合分析
Elasticsearch 支持强大的聚合功能,可以用于统计、分组和数值计算。例如,统计某个字段的平均值或最大值。
示例:聚合查询
POST /my_index/_search
{
"size": 0, # 不返回文档
"aggs": {
"average_pages": {
"avg": {
"field": "pages"
}
}
}
}
六、索引的优化与性能调优
1. 分片与副本的调优
- 分片数:过多分片会增加管理开销,分片过少则可能导致性能瓶颈。分片数应根据数据量、节点数量和查询负载进行设置。
- 副本数:副本可以提高读取性能和容错能力。在查询负载较高时,可以增加副本数。
2. 刷新与合并策略
- 刷新频率:
refresh_interval
决定了索引刷新使新数据可见的频率。频繁刷新可能会影响写入性能。可以在批量导入数据时临时关闭自动刷新。 - 段合并:Elasticsearch 在后台定期合并小段以提高搜索效率。合并过程可以通过调整
merge_policy
进行优化。
3. 索引生命周期管理(ILM)
索引生命周期管理(ILM)允许你定义索引的不同阶段(如热、温、冷、删除),并在这些阶段自动执行操作,例如缩减分片、删除数据等。
示例:ILM 策略
PUT _ilm/policy/my_policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_age": "30d",
"max_size": "50gb"
}
}
},
"warm": {
"actions": {
"shrink": {
"number_of_shards": 1
}
}
},
"cold": {
"actions": {
"freeze": {}
}
},
"delete": {
"min_age": "365d",
"actions": {
"delete": {}
}
}
}
}
}
七、索引模板与别名
1. 索引模板
索引模板用于为一组索引定义统一的设置和映射。适用于具有相似结构的大量索引。
示例:索引模板
PUT _index_template/logs_template
{
"index_patterns": ["logs-*"],
"template": {
"settings": {
"number_of_shards": 3
},
"mappings": {
"properties": {
"timestamp": {
"type": "date"
},
"message": {
"type": "text"
}
}
}
}
}
2. 索引别名
索引别名允许多个索引共享一个逻辑名称,便于索引的版本控制和无缝切换。
示例:索引别名
POST /_aliases
{
"actions": [
{
"add": {
"index": "my_index_v1",
"alias": "my_current_index"
}
},
{
"remove": {
"index": "my_index_v0",
"alias": "my_current_index"
}
}
]
}
索引生命周期(ILM)
索引生命周期管理(ILM,Index Lifecycle Management)是 Elasticsearch 提供的一项用于自动化管理索引生命周期的功能。随着数据的增长和索引数量的增加,管理索引的存储和性能变得更加复杂。ILM 允许我们为索引设置不同的生命周期阶段,以实现自动化的索引维护和优化,确保数据在不同生命周期阶段得到合理的存储、管理和最终删除。
一、为什么需要 ILM?
在实际应用中,数据的存储需求是动态变化的。随着数据的增长,索引的规模会逐渐变大,性能也会受到影响。例如,日志数据或历史记录等大量数据在刚生成时需要快速的写入和搜索性能,但随着时间的推移,老旧数据的访问频率会下降,此时我们可能希望减少存储资源或者最终删除这些数据。
ILM 允许我们: - 优化存储:通过自动将索引移到更低成本的存储介质。 - 控制性能:在数据变得不再经常访问时,通过合并分片或减少副本数来降低资源消耗。 - 自动管理数据:可以定期删除过时数据,以保持系统清洁和高效。
二、ILM 的基本概念
ILM 策略由多个阶段(Phase)组成,每个阶段定义了特定的操作。这些阶段允许我们基于索引的年龄或大小等条件自动执行操作。
1. 生命周期阶段(Phases)
ILM 中,索引的生命周期被分为四个阶段,每个阶段都有不同的目标:
- Hot Phase(热阶段):
- 用于存储和索引新数据,提供高性能的写入和搜索。
- 通常设置为快速访问的 SSD 存储。
-
操作:
rollover
、force_merge
等。 -
Warm Phase(温阶段):
- 数据已经不再频繁写入,但仍可能需要偶尔访问。
- 可以通过减少副本数、合并分片等操作来节省资源。
-
操作:
shrink
、allocate
、forcemerge
等。 -
Cold Phase(冷阶段):
- 数据极少被访问,但需要长期保留。
- 可以将索引移动到低成本存储设备,关闭索引,减少对资源的占用。
-
操作:
freeze
、allocate
、searchable_snapshot
等。 -
Delete Phase(删除阶段):
- 当数据达到预设的年龄或不再需要时,删除索引以释放存储空间。
- 操作:
delete
。
2. Rollover 机制
Rollover 是 ILM 中非常关键的机制,它允许根据预定义的条件(如索引的大小、文档数量或索引的年龄)自动创建新的索引,并将新的数据写入新的索引中。旧的索引可以根据 ILM 策略进入下一个阶段,例如移入温阶段或冷阶段。
示例:
假设一个索引最多存储 30 天的数据或者 50 GB 的容量,超过任一条件时,将创建新的索引。
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_age": "30d",
"max_size": "50gb"
}
}
}
}
}
}
三、ILM 策略的创建与使用
1. 创建 ILM 策略
ILM 策略定义了索引的生命周期,并指定了每个阶段应执行的操作。我们可以通过 PUT
请求创建自定义的 ILM 策略。
示例:创建一个简单的 ILM 策略
PUT _ilm/policy/my_policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_age": "30d",
"max_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"allocate": {
"number_of_replicas": 1
}
}
},
"cold": {
"min_age": "90d",
"actions": {
"freeze": {}
}
},
"delete": {
"min_age": "180d",
"actions": {
"delete": {}
}
}
}
}
}
2. 将策略应用到索引
为了将 ILM 策略应用到索引,可以使用索引模板或者手动在创建索引时指定 ILM 策略。
通过索引模板应用 ILM 策略
PUT _index_template/logs_template
{
"index_patterns": ["logs-*"],
"template": {
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"index.lifecycle.name": "my_policy", # 应用 ILM 策略
"index.lifecycle.rollover_alias": "logs_alias" # 配置 Rollover
}
}
}
手动应用 ILM 策略到现有索引
PUT /my_index/_settings
{
"index": {
"lifecycle": {
"name": "my_policy"
}
}
}
四、ILM 策略的操作详解
每个阶段中的具体操作都可以用来优化索引的性能和存储需求,下面详细介绍这些操作。
1. 热阶段(Hot Phase)
a. rollover
操作
rollover
是热阶段的核心操作,用于根据年龄、大小或文档数量条件创建新的索引。
"rollover": {
"max_age": "30d",
"max_size": "50gb",
"max_docs": 1000000
}
b. forcemerge
操作
forcemerge
用于将段合并为更少的段,从而提高查询性能。
"forcemerge": {
"max_num_segments": 1
}
c. set_priority
操作
通过 set_priority
设置索引的优先级,数值越高表示查询时优先使用该索引。
"set_priority": {
"priority": 100
}
2. 温阶段(Warm Phase)
a. shrink
操作
通过 shrink
操作将索引分片数量减少,这有助于节省资源。
"shrink": {
"number_of_shards": 1
}
b. allocate
操作
allocate
操作用于将索引移动到指定节点上,可以根据节点标签、节点存储类型等条件进行分配。
"allocate": {
"include": {
"box_type": "warm"
}
}
3. 冷阶段(Cold Phase)
a. freeze
操作
freeze
操作将索引冻结,使其占用更少的资源,但仍然可以进行低频的查询。
"freeze": {}
b. searchable_snapshot
操作
searchable_snapshot
将索引存储为可搜索的快照,存储在远程存储上(如 AWS S3),用于进一步减少存储成本。
"searchable_snapshot": {
"snapshot_repository": "my_repository"
}
4. 删除阶段(Delete Phase)
delete
操作
delete
操作会在索引达到指定年龄后将其删除,释放存储资源。
"delete": {}
五、ILM 策略的实际应用场景
1. 日志数据管理
对于日志数据,通常会在一段时间内频繁写入和搜索,但在数据老化后,访问频率会降低。通过 ILM,可以设置如下策略: - 在热阶段快速写入和搜索。 - 在温阶段将索引移到成本较低的存储,减少分片数量。 - 在冷阶段进一步减少资源消耗,将索引存储为只读状态。 - 最终在删除阶段定期删除过期数据。
2. 大数据分析
对于大数据分析系统,可以利用 ILM 将数据按阶段存储到不同的存储介质中: - 热阶段用于存储和分析实时数据。 - 温阶段保留几个月的数据用于历史查询。 - 冷阶段保留很久的数据但不再分析。 - 删除阶段确保数据定期删除,节省存储成本。
六、总结
ILM 是 Elasticsearch 中非常强大的工具,它允许我们自动化索引的管理,并根据索引的生命周期进行存储和性能优化。通过创建适当的策略,ILM 可以帮助我们减少存储成本,保持系统性能,并确保大规模数据的有效管理。掌握 ILM 的使用,将大大提升数据管理的效率和灵活性。
增删改查
在 Elasticsearch 中,增删改查操作是文档管理的核心操作。以下是关于 Elasticsearch 增、删、改、查操作的详细解析:
一、创建(Insert/Index)
1. 插入新文档
Elasticsearch 中创建文档有两种方式:一种是自动生成文档的 ID,另一种是手动指定 ID。
自动生成 ID
- 使用
POST
方法来索引文档,Elasticsearch 自动生成唯一的文档 ID。
POST /my_index/_doc
{
"title": "Elasticsearch 入门",
"author": "张三",
"published_date": "2024-09-20",
"pages": 300
}
手动指定 ID
- 使用
PUT
方法来索引文档,并指定文档的 ID。如果 ID 存在,文档将被更新。
PUT /my_index/_doc/1
{
"title": "Elasticsearch 高级教程",
"author": "李四",
"published_date": "2024-09-20",
"pages": 500
}
2. 批量插入文档
如果需要插入大量文档,可以使用 Bulk API 进行批量插入:
POST /my_index/_bulk
{ "index": { "_id": "1" } }
{ "title": "Elasticsearch 文档1", "author": "张三", "pages": 100 }
{ "index": { "_id": "2" } }
{ "title": "Elasticsearch 文档2", "author": "李四", "pages": 200 }
二、查询(Search)
1. 基本查询
查询所有文档
使用 GET
或 POST
方法查询索引中的所有文档。
GET /my_index/_search
{
"query": {
"match_all": {}
}
}
根据字段查询
- 使用
match
查询来根据指定字段进行查询,例如查找title
字段包含特定词汇的文档。
GET /my_index/_search
{
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
精确匹配
- 使用
term
查询进行精确匹配,不进行分词处理,适合对keyword
类型的字段进行查询。
GET /my_index/_search
{
"query": {
"term": {
"author": "张三"
}
}
}
2. 复杂查询
布尔查询(Bool Query)
- 可以结合多种查询条件,例如
must
、should
和must_not
来构建复杂的查询逻辑。
GET /my_index/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Elasticsearch" } }
],
"should": [
{ "term": { "author": "李四" } }
],
"must_not": [
{ "term": { "author": "王五" } }
]
}
}
}
范围查询(Range Query)
- 查找字段值在特定范围内的文档,如查找
pages
大于 100 的文档。
GET /my_index/_search
{
"query": {
"range": {
"pages": {
"gte": 100,
"lte": 300
}
}
}
}
3. 聚合查询(Aggregation)
- 聚合用于对数据进行统计分析,例如计算某个字段的平均值、最大值、最小值等。
GET /my_index/_search
{
"size": 0,
"aggs": {
"avg_pages": {
"avg": {
"field": "pages"
}
}
}
}
三、更新(Update)
1. 部分更新文档(Partial Update)
Elasticsearch 提供了更新文档的 API,可以只更新文档的某些字段,而无需替换整个文档。使用 _update
接口可以实现这一操作。
更新文档的部分字段
- 通过
POST
请求更新文档中特定字段。
POST /my_index/_update/1
{
"doc": {
"pages": 550
}
}
脚本更新
- 使用 Painless 脚本实现更复杂的更新逻辑,例如对数值字段进行加减运算。
POST /my_index/_update/1
{
"script": {
"source": "ctx._source.pages += params.increment",
"params": {
"increment": 50
}
}
}
2. 替换整个文档
使用 PUT
方法完全替换文档。这会删除原有的文档并替换为新文档。
PUT /my_index/_doc/1
{
"title": "Elasticsearch 完全替换",
"author": "张三",
"published_date": "2024-09-21",
"pages": 600
}
四、删除(Delete)
1. 删除单个文档
根据文档 ID 删除
- 通过
DELETE
请求删除文档。
DELETE /my_index/_doc/1
2. 根据查询条件删除
删除符合条件的文档
- 使用
_delete_by_query
API 删除满足特定查询条件的所有文档。
POST /my_index/_delete_by_query
{
"query": {
"match": {
"author": "张三"
}
}
}
3. 批量删除
批量删除多个文档
- 使用 Bulk API 批量删除多个文档。
POST /my_index/_bulk
{ "delete": { "_id": "2" } }
{ "delete": { "_id": "3" } }
五、更多操作
1. 批量操作(Bulk API)
- 批量操作 API 可以一次性进行多种操作(插入、更新、删除)。
POST /my_index/_bulk
{ "index": { "_id": "1" } }
{ "title": "Elasticsearch 文档1", "author": "张三", "pages": 100 }
{ "update": { "_id": "1" } }
{ "doc": { "pages": 150 } }
{ "delete": { "_id": "1" } }
2. 并发控制与版本管理
Elasticsearch 支持乐观并发控制和版本管理,避免多个请求同时修改同一文档导致数据不一致。
乐观并发控制
- 使用
_version
控制版本,在更新文档时可以指定版本号,确保只有特定版本的文档可以被修改。
PUT /my_index/_doc/1?version=2
{
"title": "Elasticsearch 版本控制",
"author": "李四",
"pages": 300
}
3. 删除索引
删除整个索引会删除该索引中的所有文档。
DELETE /my_index
六、总结
- 创建:可以使用
POST
和PUT
插入文档,自动生成或手动指定文档 ID。 - 查询:通过丰富的查询语法进行全文搜索、精确匹配、复杂的布尔查询及聚合操作。
- 更新:支持部分更新和完全替换,支持基于脚本的动态更新。
- 删除:可以删除单个文档、符合条件的文档或整个索引,支持批量删除操作。
ElasticSearch--文本分析
Elasticsearch 中的文本分析是全文搜索功能的核心,负责对存储和查询的文本进行预处理,以便提高搜索的准确性和效率。通过分析过程,文本可以被分解成词项(tokens),并按照一定的规则进行处理,比如去除停用词、词干提取等。本文将详细介绍 Elasticsearch 文本分析的工作原理、流程、常用分析器,以及如何自定义分析器。
一、文本分析的基本原理
文本分析过程分为以下几个阶段: 1. 字符过滤:对原始文本进行预处理,删除或替换不需要的字符。 2. 分词:将文本切分为一个个词项(token)。 3. 词项过滤:对生成的词项进行进一步处理,比如转小写、删除停用词、词干提取等。
在 Elasticsearch 中,文本字段通常分为 text
和 keyword
类型:
- text
字段会进行分词和文本分析,适合全文搜索。
- keyword
字段不会分词,适合精确匹配。
二、内置分析器
Elasticsearch 提供了多种内置分析器,常见的有以下几种:
1. Standard 分析器
这是默认的分析器,适合大多数场景。它基于 Unicode 文本分割算法,将文本分为单词,并且自动将所有词项转换为小写。
GET _analyze
{
"analyzer": "standard",
"text": "Elasticsearch is amazing!"
}
结果:
["elasticsearch", "is", "amazing"]
2. Simple 分析器
simple
分析器仅以非字母字符分隔文本,结果是纯小写的词项。
GET _analyze
{
"analyzer": "simple",
"text": "Elasticsearch: Simple Example."
}
结果:
["elasticsearch", "simple", "example"]
3. Whitespace 分析器
whitespace
分析器仅使用空格来分隔词项,不做其他处理。
GET _analyze
{
"analyzer": "whitespace",
"text": "Elasticsearch 分词示例"
}
结果:
["Elasticsearch", "分词示例"]
4. Stop 分析器
stop
分析器会删除常见的英文停用词(如 "is"、"the" 等)。
GET _analyze
{
"analyzer": "stop",
"text": "The quick brown fox"
}
结果:
["quick", "brown", "fox"]
5. Keyword 分析器
keyword
分析器不进行分词,整个输入被当作一个词项,适合做精确匹配。
GET _analyze
{
"analyzer": "keyword",
"text": "Elasticsearch Keyword Example"
}
结果:
["Elasticsearch Keyword Example"]
6. Pattern 分析器
pattern
分析器基于正则表达式来分割文本,适用于复杂的分词需求。
GET _analyze
{
"analyzer": "pattern",
"text": "Elasticsearch,Logstash,Kibana",
"pattern": ","
}
结果:
["Elasticsearch", "Logstash", "Kibana"]
三、分词器(Tokenizers)
分词器是分析器的核心组件,用来将文本切分成词项。Elasticsearch 提供了多种内置分词器:
1. Standard 分词器
默认分词器,基于 Unicode 文本分割算法。
GET _analyze
{
"tokenizer": "standard",
"text": "The quick brown fox"
}
2. Whitespace 分词器
仅根据空格切分文本。
GET _analyze
{
"tokenizer": "whitespace",
"text": "The quick brown fox"
}
3. Letter 分词器
根据字母切分文本,非字母字符将被移除。
GET _analyze
{
"tokenizer": "letter",
"text": "100 Quick Brown Foxes!"
}
4. Keyword 分词器
将整个输入作为一个词项,无论内容多长。
GET _analyze
{
"tokenizer": "keyword",
"text": "Elastic Keyword Example"
}
5. Pattern 分词器
通过正则表达式来分割文本。
GET _analyze
{
"tokenizer": "pattern",
"text": "2024-09-20",
"pattern": "-"
}
四、词项过滤器(Token Filters)
词项过滤器用于对分词器生成的词项进行处理。Elasticsearch 提供了多种常用的过滤器。
1. Lowercase 过滤器
将所有词项转换为小写。
GET _analyze
{
"tokenizer": "whitespace",
"filter": ["lowercase"],
"text": "Quick Brown Fox"
}
结果:
["quick", "brown", "fox"]
2. Stop 过滤器
移除常见的停用词(如 "the", "is" 等)。
GET _analyze
{
"tokenizer": "standard",
"filter": ["stop"],
"text": "The quick brown fox"
}
结果:
["quick", "brown", "fox"]
3. Stemmer 过滤器
使用词干提取算法将单词还原为其词根形式。
GET _analyze
{
"tokenizer": "standard",
"filter": ["porter_stem"],
"text": "running jumps"
}
结果:
["run", "jump"]
4. Synonym 过滤器
将同义词替换或扩展为标准词项。
PUT /my_index
{
"settings": {
"analysis": {
"filter": {
"synonym_filter": {
"type": "synonym",
"synonyms": ["quick,fast"]
}
},
"analyzer": {
"synonym_analyzer": {
"tokenizer": "whitespace",
"filter": ["synonym_filter"]
}
}
}
}
}
GET /my_index/_analyze
{
"analyzer": "synonym_analyzer",
"text": "quick"
}
结果:
["quick", "fast"]
5. Length 过滤器
过滤掉过短或过长的词项。
GET _analyze
{
"tokenizer": "whitespace",
"filter": [
{
"type": "length",
"min": 3,
"max": 5
}
],
"text": "Elasticsearch is fast"
}
结果:
["fast"]
五、自定义分析器
除了内置的分析器,你可以根据需要自定义分析器。一个分析器由字符过滤器、分词器、词项过滤器等组件组成。
1. 创建自定义分析器
在创建索引时可以指定自定义分析器。
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom",
"tokenizer": "whitespace",
"filter": ["lowercase", "stop"]
}
}
}
}
}
2. 使用自定义分析器
GET /my_index/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "The Quick Brown Fox"
}
结果:
["quick", "brown", "fox"]
六、实战案例
1. 中文分词(IK 分词器)
Elasticsearch 提供了支持中文分词的插件,最常用的 IK 分词器。安装并配置 IK 分词器后,可以使用它处理中文文本的分词。
安装 IK 分词器
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/vX.X.X/analysis-ik-X.X.X.zip
使用 IK 分词器
GET /_analyze
{
"analyzer": "ik_smart",
"text": "这是一个中文分词的示例"
}
结果:
["这是", "一个", "中文", "分词", "的", "示例"]
2. 实现拼音搜索
在中文搜索场景中,拼音搜索也是常见需求。可以通过拼音分词器实现。
七、总结
通过以上内容,您已经全面了解了 Elasticsearch 的文本分析过程和如何自定义分析器。关键点包括: - 分析器 将文本转化为可搜索的词项。 - 分词器 决定了文本如何切分为词项。 - 词项过滤器 则进一步处理这些词项,比如删除停用词或进行词干提取。 - 自定义分析器 让你可以根据特定的需求灵活构建分析流程。
ElasticSearch--搜索数据
Elasticsearch 搜索数据:从入门到精通详解
Elasticsearch 是一个开源、分布式的搜索和分析引擎,广泛应用于日志分析、全文搜索、数据分析等领域。它基于 Apache Lucene,并通过 RESTful API 进行交互。本文将带你深入了解 Elasticsearch 搜索数据的全流程,逐步从基础到高级功能进行讲解,帮助你系统地掌握 Elasticsearch 的搜索能力。
一、搜索的基础概念
在 Elasticsearch 中,数据存储于索引(Index)中。索引由多个分片(Shards)组成,分片是 Elasticsearch 的最小存储单元,允许 Elasticsearch 横向扩展处理和存储大量数据。
数据查询和搜索时,Elasticsearch 会使用倒排索引(Inverted Index),它是一种将文档中的单词映射到其出现位置的数据结构。搜索时,通过倒排索引快速找到匹配词汇的位置。
查询与索引结构: - Index(索引): 数据集合,相当于关系型数据库的表。 - Document(文档): 存储的数据单位,相当于表中的一行。 - Field(字段): 文档中的键值对,相当于表中的列。
1. 文档与字段
在 Elasticsearch 中,每个文档是 JSON 格式的,字段则存储在文档中。例如:
{
"title": "Elasticsearch入门",
"author": "张三",
"published_year": 2023,
"content": "Elasticsearch 是一个分布式搜索引擎..."
}
文档映射(Mapping) 决定了字段类型(如 text
、keyword
、date
等),并影响搜索行为。默认情况下,Elasticsearch 自动为字段分配类型。
二、基本查询
Elasticsearch 支持多种查询语法,满足各种搜索需求。最常见的查询是 match
和 term
查询,它们用于执行全文和精确匹配搜索。
1. 查询所有文档
使用 match_all
可以查询索引中的所有文档:
GET /my_index/_search
{
"query": {
"match_all": {}
}
}
2. 字段匹配查询(全文搜索)
match
查询对文本字段进行分词和分析,常用于全文搜索。搜索时,Elasticsearch 会将查询字符串与文档中的分词结果进行比对。
GET /my_index/_search
{
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
3. 精确匹配查询
term
查询不会对字段进行分词,适用于精确匹配的场景。它通常用于 keyword
类型的字段或数值字段。
GET /my_index/_search
{
"query": {
"term": {
"status": "published"
}
}
}
4. 多字段查询
multi_match
查询允许在多个字段上搜索一个词,适合在多个文本字段中查找内容。
GET /my_index/_search
{
"query": {
"multi_match": {
"query": "Elasticsearch",
"fields": ["title", "description"]
}
}
}
5. 布尔查询
bool
查询用于组合多个查询条件,它允许使用 must
、should
和 must_not
等逻辑条件。
GET /my_index/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Elasticsearch" } }
],
"should": [
{ "match": { "author": "张三" } }
],
"must_not": [
{ "term": { "status": "draft" } }
]
}
}
}
6. 范围查询
range
查询可以用来查找数值或日期在指定范围内的文档。
GET /my_index/_search
{
"query": {
"range": {
"published_year": {
"gte": 2020,
"lte": 2024
}
}
}
}
三、进阶查询技巧
1. 聚合(Aggregation)
聚合用于对数据进行统计分析,它类似于 SQL 中的 GROUP BY
语法。常见的聚合类型有 avg
、max
、min
、sum
、terms
等。
1.1 计算字段的平均值
GET /my_index/_search
{
"size": 0,
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
1.2 词频统计(分组)
GET /my_index/_search
{
"size": 0,
"aggs": {
"status_count": {
"terms": {
"field": "status"
}
}
}
}
2. 搜索结果高亮显示
highlight
功能可以将搜索到的词项用特定的标签标注,方便用户在结果中快速定位关键信息。
GET /my_index/_search
{
"query": {
"match": { "content": "Elasticsearch" }
},
"highlight": {
"fields": {
"content": {}
}
}
}
3. 函数评分查询(Function Score)
使用 function_score
可以根据自定义的评分函数来调整文档的相关性评分。例如,你可以基于某个数值字段(如 popularity
)对文档进行加权排序。
GET /my_index/_search
{
"query": {
"function_score": {
"query": { "match": { "title": "Elasticsearch" } },
"field_value_factor": {
"field": "popularity",
"factor": 1.5,
"modifier": "sqrt"
}
}
}
}
4. 跨索引查询
Elasticsearch 支持跨索引搜索,允许在多个索引中执行同一查询。这在多个数据源上进行搜索时非常有用。
GET /index1,index2/_search
{
"query": {
"match": { "content": "搜索" }
}
}
四、搜索优化
当处理大规模数据时,搜索性能的优化至关重要。Elasticsearch 提供了多种优化手段,包括分页查询、滚动查询、缓存和分片优化。
1. 分页与滚动查询
分页查询用于返回较小的数据集,而滚动查询则用于处理大量数据。
1.1 分页查询
通过 from
和 size
控制返回的结果集大小:
GET /my_index/_search
{
"from": 0,
"size": 10,
"query": {
"match_all": {}
}
}
1.2 滚动查询
滚动查询用于从非常大的结果集中分批次获取数据。第一次执行查询时会返回一个 scroll_id
,通过它可以获取下一批结果。
POST /my_index/_search?scroll=1m
{
"size": 100,
"query": {
"match_all": {}
}
}
# 使用 scroll_id 获取下一批结果
POST /_search/scroll
{
"scroll": "1m",
"scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAA"
}
2. 结果缓存与过滤器
为了加速查询性能,Elasticsearch 会自动缓存某些查询结果。对于常见的过滤器(filter
),它不会参与文档评分计算,因此比普通查询更快。
GET /my_index/_search
{
"query": {
"bool": {
"filter": {
"term": { "status": "published" }
}
}
}
}
3. 分片优化
索引的分片配置会直接影响查询性能。分片数量过多或过少都会影响性能,分片应根据数据量、硬件资源进行合理分配。
五、实战案例
1. 日志搜索
Elasticsearch 常用于日志管理和分析。下面是一个示例,查询过去 24 小时内的错误日志:
GET /logs/_search
{
"query": {
"bool": {
"must": {
"match": { "level": "error" }
},
"filter": {
"range": {
"timestamp": {
"gte": "now-24h",
"lte": "now"
}
}
}
}
}
}
2. 电商产品搜索
电商网站常用 Elasticsearch 进行产品搜索,并根据用户评分和价格进行排序。下面是一个示例,搜索产品并按评分降序、价格升序排列:
GET /products/_search
{
"query": {
"multi_match": {
"query": "laptop",
"fields": ["title", "description", "category"]
}
},
"sort": [
{ "rating": "desc" },
{ "price": "asc" }
]
}
六、总结
通过本教程的学习,您已经掌握了 Elasticsearch 搜索的基础和高级技巧。包括:
- 查询类型:从
match
、term
到布尔查询。 - 高级功能:聚合、评分调整、搜索结果高亮等。
- 优化策略:缓存、分页、分片优化等。
ElasticSearch--聚集统计
Elasticsearch 聚合统计:从入门到精通详解
Elasticsearch 的聚合(Aggregation)功能是一个强大的工具,用于在索引数据上执行复杂的统计分析和数据汇总。聚合类似于 SQL 中的 GROUP BY
和聚合函数(如 COUNT
、SUM
、AVG
等),但它不仅限于这些功能,还能执行更高级的分组、嵌套分析、地理计算等操作。
本文将详细介绍 Elasticsearch 中的聚合功能,从基础到高级应用,帮助你系统掌握 Elasticsearch 的聚合统计。
一、什么是聚合(Aggregation)?
在 Elasticsearch 中,聚合是对数据进行统计、分组、筛选的工具。聚合操作允许我们在索引中的文档上进行实时计算和分析。例如,可以统计某个字段的值总和、计算字段的平均值、找到文档中某个字段的最大值等。
Elasticsearch 聚合类型包括: - 度量聚合(Metric Aggregation):用于生成统计数据(如总数、最大值、最小值、平均值等)。 - 桶聚合(Bucket Aggregation):将文档分组到“桶”中,每个桶中包含符合某一条件的文档。 - 管道聚合(Pipeline Aggregation):基于其他聚合结果进行二次聚合。 - 矩阵聚合(Matrix Aggregation):用于计算多字段之间的关联数据。
二、度量聚合(Metric Aggregation)
度量聚合生成关于数据的单一数值,比如总数、平均值、最小值等。度量聚合的结果是全局性的,不会根据文档进行分组。
1. count
:文档总数
文档总数的聚合操作可以使用 value_count
来统计某个字段出现的次数。
GET /my_index/_search
{
"size": 0,
"aggs": {
"total_docs": {
"value_count": {
"field": "id"
}
}
}
}
2. sum
:求和
使用 sum
聚合来统计数值字段的总和,适用于计算数值总和的场景,例如总收入、销售额等。
GET /my_index/_search
{
"size": 0,
"aggs": {
"total_sales": {
"sum": {
"field": "price"
}
}
}
}
3. avg
:平均值
avg
聚合用于计算数值字段的平均值。例如,计算某个产品的平均销售价格。
GET /my_index/_search
{
"size": 0,
"aggs": {
"average_price": {
"avg": {
"field": "price"
}
}
}
}
4. min
和 max
:最小值和最大值
min
和 max
聚合分别用于获取字段的最小值和最大值。例如:
GET /my_index/_search
{
"size": 0,
"aggs": {
"min_price": {
"min": {
"field": "price"
}
},
"max_price": {
"max": {
"field": "price"
}
}
}
}
5. stats
:统计
stats
聚合返回一个字段的多种统计结果,包括最小值、最大值、平均值、总和和文档计数。
GET /my_index/_search
{
"size": 0,
"aggs": {
"price_stats": {
"stats": {
"field": "price"
}
}
}
}
三、桶聚合(Bucket Aggregation)
桶聚合用于将文档按某些条件分组,类似于 SQL 中的 GROUP BY
。每个桶包含符合条件的文档。常见的桶聚合有 terms
、range
、histogram
等。
1. terms
聚合:字段分组
terms
聚合类似于 SQL 中的 GROUP BY
,根据某个字段的值将文档进行分组。例如,根据 status
字段将文档分组并统计每个状态的文档数量:
GET /my_index/_search
{
"size": 0,
"aggs": {
"status_count": {
"terms": {
"field": "status"
}
}
}
}
2. range
聚合:范围分组
range
聚合用于根据数值或日期字段的范围将文档分组。例如,根据价格的范围对产品进行分组:
GET /my_index/_search
{
"size": 0,
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50 },
{ "from": 50, "to": 200 },
{ "from": 200 }
]
}
}
}
}
3. date_histogram
聚合:按时间分组
date_histogram
聚合根据时间字段将文档分组,常用于按天、按月、按年等时间间隔聚合文档。例如,按月统计文档数量:
GET /my_index/_search
{
"size": 0,
"aggs": {
"monthly_sales": {
"date_histogram": {
"field": "sale_date",
"calendar_interval": "month"
}
}
}
}
4. 嵌套桶聚合
可以将桶聚合嵌套在另一个桶聚合中。例如,先按 status
分组,再按 price
的范围分组:
GET /my_index/_search
{
"size": 0,
"aggs": {
"status_buckets": {
"terms": {
"field": "status"
},
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50 },
{ "from": 50, "to": 200 },
{ "from": 200 }
]
}
}
}
}
}
}
四、管道聚合(Pipeline Aggregation)
管道聚合用于基于其他聚合结果进行二次聚合。例如,可以在 date_histogram
聚合的基础上计算平均值或趋势。
1. avg_bucket
聚合
avg_bucket
聚合用于计算桶聚合结果的平均值。例如,计算 monthly_sales
中每个月的平均销售额:
GET /my_index/_search
{
"size": 0,
"aggs": {
"monthly_sales": {
"date_histogram": {
"field": "sale_date",
"calendar_interval": "month"
},
"aggs": {
"sales_sum": {
"sum": {
"field": "sales_amount"
}
}
}
},
"avg_sales_per_month": {
"avg_bucket": {
"buckets_path": "monthly_sales>sales_sum"
}
}
}
}
2. derivative
聚合
derivative
聚合用于计算桶中值的变化量,类似于求导。例如,计算销售额的增长或下降趋势:
GET /my_index/_search
{
"size": 0,
"aggs": {
"monthly_sales": {
"date_histogram": {
"field": "sale_date",
"calendar_interval": "month"
},
"aggs": {
"sales_sum": {
"sum": {
"field": "sales_amount"
}
}
}
},
"sales_derivative": {
"derivative": {
"buckets_path": "monthly_sales>sales_sum"
}
}
}
}
五、矩阵聚合(Matrix Aggregation)
矩阵聚合用于计算多个字段之间的关联数据,适合处理金融、统计等复杂的场景。
1. matrix_stats
聚合
matrix_stats
聚合用于计算多个字段之间的协方差、相关系数、均值等。适用于分析多个数值字段之间的关系。
GET /my_index/_search
{
"size": 0,
"aggs": {
"matrix_stats_agg": {
"matrix_stats": {
"fields": ["price", "sales", "quantity"]
}
}
}
}
六、实战案例
1. 销售数据分析
假设我们有一个销售数据索引,其中包含产品的销售日期、销售金额和销售数量。我们希望分析每月的销售额、每月的销售额增长趋势和每月的平均销售额。
GET /sales/_search
{
"size": 0,
"aggs": {
"monthly_sales": {
"date_histogram": {
"field": "sale_date",
"calendar_interval": "month"
},
"aggs": {
"sales_sum": {
"sum": {
"field": "sales_amount"
}
}
}
},
"sales_trend": {
"derivative": {
"buckets_path": "monthly_sales>sales_sum"
}
},
"avg_sales_per_month": {
"avg_bucket": {
"buckets_path": "monthly_sales>sales_sum"
}
}
}
}
2. 网站日志分析
对于网站日志,可以使用 Elasticsearch 聚合来统计每日的访问量和错误请求数。例如:
GET /logs/_search
{
"size": 0,
"aggs": {
"daily_requests": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "day"
},
"aggs": {
"status_codes": {
"terms": {
"field": "status_code"
}
}
}
}
}
}
七、总结
Elasticsearch 的聚合功能强大而灵活,从简单的数值统计到复杂的分组和嵌套分析,再到管道聚合和矩阵分析,能够满足各种数据分析场景。在实际应用中,聚合操作可以帮助我们快速对海量数据进行实时统计和分析,为业务决策提供数据支持。通过掌握本文介绍的聚合技术,您可以在实际项目中高效地处理数据并进行深入的统计分析。
ElasticSearch--父子关联
Elasticsearch 父子关联:详解
Elasticsearch 不仅支持简单的文档存储与搜索,还提供了用于复杂数据关系的功能,其中之一就是 父子关联(Parent-Child Relationship)。父子关联允许我们在不同类型的文档之间建立层次关系,而不必将所有相关信息存储在同一个文档中。通过父子关联,我们可以避免数据冗余,并支持更灵活的查询和管理。
本文将详细讲解 Elasticsearch 中的父子关联,包括其使用场景、如何配置父子关联、相关查询操作以及与嵌套关系的比较。
一、父子关联的使用场景
父子关联的主要使用场景是处理具有层次结构的复杂数据。例如:
- 电子商务系统:产品作为父文档,评论作为子文档。一个产品可以有多个评论,但评论和产品是独立的,具有不同的字段和索引要求。
- 社交媒体:用户作为父文档,用户发布的帖子作为子文档。一个用户可以有多个帖子,帖子和用户有不同的属性,但它们是相关联的。
父子关联的优势在于: - 节省存储:将子文档与父文档分开存储,可以避免重复存储相同的父文档数据。 - 灵活查询:可以独立查询父文档或子文档,甚至可以基于父子关系进行复杂的查询。
二、父子关联的配置
在 Elasticsearch 中,父子关联是通过 join
数据类型来实现的。join
字段允许在同一个索引中定义父文档和子文档的关系。
1. 创建父子关联映射
在 Elasticsearch 中,不再支持直接创建多个类型(_type
),因此父子关联是在一个索引中实现的。使用 join
类型来定义父子关系时,可以定义父文档和子文档的结构。下面是一个电子商务场景的示例,产品是父文档,评论是子文档:
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"category": {
"type": "keyword"
},
"my_join_field": {
"type": "join",
"relations": {
"product": "review"
}
}
}
}
}
在上面的映射中,my_join_field
是用来表示父子关系的字段,其中定义了 product
是父文档,review
是子文档。
2. 索引父文档
父文档是普通的文档,只是需要在 my_join_field
中声明类型为 product
,例如:
POST /products/_doc/1
{
"name": "Laptop",
"category": "Electronics",
"my_join_field": {
"name": "product"
}
}
3. 索引子文档
在索引子文档时,必须指定该子文档的父文档 parent
,并在 my_join_field
中指定子文档的类型为 review
。例如,给上面的产品添加评论:
POST /products/_doc/2?routing=1
{
"reviewer": "John Doe",
"rating": 5,
"comment": "Great laptop!",
"my_join_field": {
"name": "review",
"parent": "1"
}
}
注意这里的 routing=1
表示该子文档会与父文档 1
保存在相同的分片中,这样可以提高查询效率。
三、父子关联的查询
父子文档可以通过特殊的查询方式关联起来。常见的查询类型有 has_parent
和 has_child
查询。
1. has_child
查询
has_child
查询用于查找父文档,该父文档必须有特定条件的子文档。例如,查找那些有评分为 5 的评论的产品:
GET /products/_search
{
"query": {
"has_child": {
"type": "review",
"query": {
"term": {
"rating": 5
}
}
}
}
}
该查询会返回所有有评分为 5 的评论的父文档(产品)。
2. has_parent
查询
has_parent
查询用于查找子文档,其父文档必须符合特定条件。例如,查找所有属于“Electronics”类别的产品的评论:
GET /products/_search
{
"query": {
"has_parent": {
"parent_type": "product",
"query": {
"term": {
"category": "Electronics"
}
}
}
}
}
该查询会返回所有其父文档类别为 “Electronics” 的子文档(评论)。
四、父子关联 vs 嵌套类型
在处理复杂数据时,父子关联和嵌套类型都可以用来处理文档之间的关联,但它们有不同的优缺点和适用场景。
1. 嵌套类型(Nested Type)
嵌套类型用于在单个文档内部存储嵌套的对象(子文档)。这种方式更适合关系紧密的对象。例如,用户的多个地址作为嵌套对象存储在用户文档中。
优点: - 所有数据存储在同一个文档中,查询速度快。 - 简单的嵌套结构,适用于数据不需要单独管理的场景。
缺点: - 当嵌套对象较多时,文档变得庞大,影响索引性能。
2. 父子关联(Parent-Child)
父子关联允许子文档独立于父文档存储,适合那些需要频繁修改子文档而不想影响父文档的场景。
优点: - 子文档和父文档独立存储,修改其中一个不会影响另一个。 - 适用于数据结构灵活且子文档较多的场景。
缺点: - 查询效率相对嵌套类型较低,因为需要跨文档进行关联查询。 - 需要正确配置路由以优化性能。
选择嵌套类型还是父子关联?
- 嵌套类型:适合关系紧密且文档较小的场景,且查询效率更高。
- 父子关联:适合数据更新频繁且父子数据可以独立存储的场景。
五、父子关联的性能优化
父子关联查询的性能较为复杂,因为它们涉及多个文档之间的关联查询。为了提高查询性能,可以考虑以下优化措施:
-
适当的路由设置:父子文档应存储在同一个分片上,可以通过
routing
参数将子文档与父文档存储在相同的分片。 -
减少父子关联的使用:如果不需要频繁的子文档更新,或者可以将数据平展化,可以考虑使用嵌套类型或将数据存储在同一个文档中。
-
聚合优化:如果需要在父子文档上进行聚合操作,应该尽量减少父子关联的深度,避免过多的嵌套查询。
六、总结
父子关联是 Elasticsearch 中处理复杂数据关系的有效工具,特别适合那些需要独立存储但又存在关联的文档。例如,在电子商务、社交媒体等场景下,父子关联可以帮助我们高效管理和查询数据。通过掌握父子关联的使用和查询技巧,我们可以更灵活地处理复杂的层次数据。
在选择父子关联和嵌套类型时,应该根据实际场景进行权衡。如果数据更新频繁且关系不紧密,父子关联是一个很好的选择;而如果数据关系紧密且查询更频繁,嵌套类型则更为合适。