ElasticSearch自动创建索引的机制
2021-02-262.3k 阅读
ElasticSearch 自动创建索引的机制
ElasticSearch 索引基础概念
在深入探讨 ElasticSearch 自动创建索引机制之前,我们先来回顾一下索引的基本概念。在 ElasticSearch 中,索引是文档的集合,类似于传统关系型数据库中的数据库概念。每个索引都有一个唯一的名称,通过这个名称可以对索引中的文档进行各种操作,如添加、查询、更新和删除。
ElasticSearch 使用倒排索引结构来存储和检索数据。倒排索引是一种基于单词的索引结构,它将每个单词映射到包含该单词的文档列表。这种结构使得 ElasticSearch 能够快速地根据关键词找到相关的文档,从而实现高效的搜索功能。
自动创建索引的触发条件
- 文档写入操作
- 当向 ElasticSearch 发送一个文档写入请求时,如果目标索引不存在,ElasticSearch 会自动创建该索引。例如,使用 ElasticSearch 的 REST API 发送如下的 PUT 请求:
在上述请求中,如果PUT /my_index/_doc/1 { "title": "示例文档", "content": "这是一个自动创建索引的示例文档" }
my_index
索引不存在,ElasticSearch 会自动创建my_index
索引,并将指定的文档添加到该索引中。- 从代码层面看,在 Java 中使用 Elasticsearch High - Level REST Client 也可以实现类似的操作:
这段 Java 代码同样会在import org.apache.http.HttpHost; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; public class AutoCreateIndexExample { public static void main(String[] args) throws IOException { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http"))); IndexRequest request = new IndexRequest("my_index") .id("1") .source("{\"title\":\"示例文档\",\"content\":\"这是一个自动创建索引的示例文档\"}", XContentType.JSON); IndexResponse response = client.index(request, RequestOptions.DEFAULT); client.close(); } }
my_index
索引不存在时自动创建该索引并添加文档。 - Mapping 定义操作
- 虽然文档写入是触发自动创建索引最常见的方式,但在某些情况下,直接定义 Mapping 也可能触发索引的自动创建。例如,通过 PUT Mapping API 为一个不存在的索引定义 Mapping:
在上述请求中,如果PUT /new_index/_mapping { "properties": { "name": { "type": "text" }, "age": { "type": "integer" } } }
new_index
索引不存在,ElasticSearch 会自动创建该索引,并应用定义的 Mapping。- 在 Python 中使用 Elasticsearch 库可以这样实现:
此 Python 代码同样会在from elasticsearch import Elasticsearch es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) mapping = { "properties": { "name": { "type": "text" }, "age": { "type": "integer" } } } es.indices.put_mapping(index='new_index', body=mapping)
new_index
索引不存在时自动创建索引并设置 Mapping。
自动创建索引的配置参数
- index.mapper.dynamic
- 这个参数控制了在文档写入时,ElasticSearch 如何动态地为新字段添加 Mapping。它有三个取值:
true
、false
和strict
。 - true(默认值):当文档中出现新字段时,ElasticSearch 会自动为该字段添加 Mapping。例如,有如下文档写入请求:
如果PUT /dynamic_index/_doc/1 { "new_field": "新字段的值" }
dynamic_index
索引已经存在,且index.mapper.dynamic
为true
,ElasticSearch 会自动为new_field
添加 Mapping,根据字段值的类型推断其数据类型,这里new_field
会被推断为text
类型。- false:当文档中出现新字段时,ElasticSearch 不会自动为该字段添加 Mapping。新字段的数据不会被索引,但是会在
_source
字段中保留。例如:
假设PUT /no_dynamic_index/_doc/1 { "new_field": "新字段的值" }
no_dynamic_index
索引存在且index.mapper.dynamic
为false
,new_field
不会被索引,在搜索时无法通过new_field
进行查询,但在获取文档的_source
时可以看到new_field
及其值。- strict:当文档中出现新字段时,ElasticSearch 会抛出异常,拒绝写入该文档。例如:
如果PUT /strict_index/_doc/1 { "new_field": "新字段的值" }
strict_index
索引存在且index.mapper.dynamic
为strict
,上述请求会失败,因为new_field
是新出现的字段。 - 这个参数控制了在文档写入时,ElasticSearch 如何动态地为新字段添加 Mapping。它有三个取值:
- action.auto_create_index
- 这个参数控制是否允许自动创建索引。它可以接受一个逗号分隔的索引名称模式列表。例如,设置
action.auto_create_index: my_index*,other_index
,表示只有以my_index
开头的索引和other_index
索引可以自动创建。 - 如果设置为
false
,则完全禁用自动创建索引功能。任何针对不存在索引的写入或 Mapping 操作都会失败。例如,尝试写入一个不存在的索引:
当PUT /disabled_auto_index/_doc/1 { "data": "一些数据" }
action.auto_create_index
为false
时,上述请求会返回错误,提示索引不存在且自动创建被禁用。 - 这个参数控制是否允许自动创建索引。它可以接受一个逗号分隔的索引名称模式列表。例如,设置
自动创建索引的流程
- 索引名称验证
- 当触发自动创建索引的操作时,ElasticSearch 首先会验证索引名称是否符合命名规则。索引名称必须小写,不能包含空格、逗号、冒号等特殊字符,且长度不能超过 255 个字符。例如,
my_index
是一个合法的索引名称,而MyIndex
(大写开头)、my,index
(包含逗号)都是不合法的。 - 如果索引名称不合法,ElasticSearch 会立即返回错误,不会进行自动创建索引的后续操作。
- 当触发自动创建索引的操作时,ElasticSearch 首先会验证索引名称是否符合命名规则。索引名称必须小写,不能包含空格、逗号、冒号等特殊字符,且长度不能超过 255 个字符。例如,
- 索引模板匹配
- ElasticSearch 会尝试查找与要创建的索引名称匹配的索引模板。索引模板可以定义索引的各种设置和 Mapping 规则。例如,可以定义一个索引模板
my_template
:
当尝试自动创建PUT _template/my_template { "index_patterns": ["my_index*"], "settings": { "number_of_shards": 3, "number_of_replicas": 1 }, "mappings": { "properties": { "common_field": { "type": "text" } } } }
my_index1
索引时,由于my_index1
匹配my_index*
模式,ElasticSearch 会应用my_template
模板中的设置和 Mapping 来创建索引。- 如果没有匹配的索引模板,ElasticSearch 会使用默认的设置和 Mapping 来创建索引。默认情况下,索引会有 1 个主分片和 1 个副本分片,并且文档中的字段会根据数据类型自动推断 Mapping。
- ElasticSearch 会尝试查找与要创建的索引名称匹配的索引模板。索引模板可以定义索引的各种设置和 Mapping 规则。例如,可以定义一个索引模板
- 索引创建与初始化
- 一旦确定了索引的设置和 Mapping(无论是通过模板还是默认值),ElasticSearch 就会在集群中创建索引。它会在每个相关的节点上分配主分片和副本分片。
- 例如,对于一个有 3 个节点的集群,当创建一个具有 3 个主分片和 1 个副本分片的索引时,ElasticSearch 会将 3 个主分片分配到不同的节点上,并为每个主分片创建一个副本分片,副本分片会分配到与主分片不同的节点上,以实现数据的冗余和高可用性。
- 在索引创建完成后,ElasticSearch 会初始化相关的数据结构,如倒排索引,为后续的文档写入和搜索操作做好准备。
自动创建索引的注意事项
- 性能影响
- 频繁的自动创建索引操作可能会对 ElasticSearch 集群的性能产生影响。每次创建索引时,ElasticSearch 需要进行一系列的操作,如分配分片、初始化数据结构等,这些操作会消耗集群的资源,包括 CPU、内存和网络带宽。
- 例如,如果在短时间内大量自动创建索引,可能会导致集群负载过高,从而影响其他正常的搜索和写入操作的性能。为了避免这种情况,可以尽量提前规划好索引结构,减少不必要的自动创建索引操作。
- 数据一致性
- 在自动创建索引的过程中,如果多个节点同时尝试创建同一个索引,可能会出现数据一致性问题。虽然 ElasticSearch 有一些机制来处理这种情况,但在极端情况下,可能会导致索引创建失败或出现不一致的状态。
- 为了确保数据一致性,可以在创建索引之前先检查索引是否存在。例如,在 Java 中可以使用如下代码检查索引是否存在:
这样可以避免多个节点同时创建同一个索引带来的数据一致性问题。import org.apache.http.HttpHost; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.indices.GetIndexRequest; import java.io.IOException; public class IndexExistsExample { public static void main(String[] args) throws IOException { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http"))); GetIndexRequest request = new GetIndexRequest("my_index"); boolean exists = client.indices().exists(request, RequestOptions.DEFAULT); if (!exists) { // 执行创建索引操作 } client.close(); } }
- 安全风险
- 自动创建索引可能带来一定的安全风险。如果恶意用户能够向 ElasticSearch 发送写入请求,他们可能会利用自动创建索引的功能创建大量无用的索引,从而消耗集群资源,甚至进行数据泄露等恶意行为。
- 为了防范这种风险,需要对 ElasticSearch 进行严格的访问控制,如使用身份验证和授权机制。可以通过 Elasticsearch 的 X - Pack 插件启用身份验证和授权,只有经过授权的用户才能进行索引创建和文档写入操作。例如,配置基本身份验证后,客户端在发送请求时需要提供用户名和密码:
这样可以有效防止未经授权的自动创建索引等恶意操作。import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; public class SecuredClientExample { public static void main(String[] args) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials("username", "password")); RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http")) .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider))); // 使用 client 进行操作 try { client.close(); } catch (Exception e) { e.printStackTrace(); } } }
自动创建索引与动态 Mapping 的关系
- 动态 Mapping 依赖自动创建索引
- 动态 Mapping 是在自动创建索引过程中,或者在已存在索引的文档写入时,为新字段自动生成 Mapping 的机制。当自动创建索引时,如果
index.mapper.dynamic
参数为true
(默认值),ElasticSearch 不仅会创建索引,还会根据文档中的字段自动生成 Mapping。 - 例如,当自动创建一个索引并写入如下文档:
ElasticSearch 会自动为PUT /dynamic_mapping_index/_doc/1 { "new_text_field": "文本值", "new_number_field": 123 }
new_text_field
生成text
类型的 Mapping,为new_number_field
生成integer
类型的 Mapping。 - 动态 Mapping 是在自动创建索引过程中,或者在已存在索引的文档写入时,为新字段自动生成 Mapping 的机制。当自动创建索引时,如果
- 动态 Mapping 对自动创建索引的影响
- 动态 Mapping 的设置会影响自动创建索引的最终形态。如果将
index.mapper.dynamic
设置为false
或strict
,在自动创建索引时,即使文档中包含新字段,也不会为这些新字段生成 Mapping(false
时新字段不索引但保留在_source
,strict
时拒绝写入)。 - 这意味着,动态 Mapping 的设置决定了自动创建索引时对文档字段的处理方式,进而影响索引的结构和后续的搜索功能。例如,在
index.mapper.dynamic
为false
的情况下创建索引并写入文档后,后续无法通过新字段进行搜索,因为新字段没有被索引。
- 动态 Mapping 的设置会影响自动创建索引的最终形态。如果将
实际应用场景中的自动创建索引
- 日志管理系统
- 在日志管理系统中,自动创建索引非常实用。通常,日志数据会按照时间进行分区存储,每天可能会产生一个新的日志索引。例如,每天凌晨,日志收集系统会将当天的日志数据发送到 ElasticSearch。
- 如果使用自动创建索引功能,日志收集系统只需要将日志文档发送到形如
log - yyyy - MM - dd
的索引中,ElasticSearch 会自动创建不存在的日期索引。例如,在 Python 中可以这样实现:
这种方式简化了日志索引的管理,无需手动提前创建每个日期的索引。from elasticsearch import Elasticsearch from datetime import datetime es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) today = datetime.now().strftime('%Y-%m-%d') index_name = f'log - {today}' log_data = { "timestamp": datetime.now().isoformat(), "message": "这是一条示例日志" } es.index(index=index_name, body=log_data)
- 物联网数据收集
- 物联网环境中,大量的设备会实时上传数据。每个设备可能会产生不同类型的数据,而且设备数量可能动态变化。自动创建索引可以很好地适应这种场景。
- 例如,每个物联网设备可以将数据发送到以设备 ID 命名的索引中。当新设备开始上传数据时,ElasticSearch 会自动创建对应的索引。在 Java 中可以这样模拟:
当有新的设备加入并发送数据时,ElasticSearch 会自动为其创建索引,方便对不同设备的数据进行存储和管理。import org.apache.http.HttpHost; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; public class IoTDataIndexing { public static void main(String[] args) throws IOException { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http"))); String deviceId = "device_123"; IndexRequest request = new IndexRequest(deviceId) .source("{\"temperature\":25,\"humidity\":60}", XContentType.JSON); IndexResponse response = client.index(request, RequestOptions.DEFAULT); client.close(); } }
总结自动创建索引机制的优缺点
- 优点
- 便捷性:自动创建索引极大地简化了开发和运维流程。开发人员无需手动提前创建每个可能用到的索引,只需要专注于数据的写入和业务逻辑的实现。例如,在快速迭代的 Web 应用开发中,新的业务需求可能会导致数据结构的频繁变化,自动创建索引可以轻松应对这种情况,减少了开发过程中的索引管理工作量。
- 灵活性:能够适应动态变化的数据环境。如在物联网场景中,新设备的不断加入和数据类型的动态变化,自动创建索引可以实时为新数据创建合适的存储结构,保证数据的及时存储和处理。
- 缺点
- 性能开销:如前文所述,频繁的自动创建索引操作会消耗集群资源,影响性能。在大规模集群中,如果大量索引同时自动创建,可能会导致集群负载过高,影响正常的搜索和写入操作。
- 潜在风险:存在安全风险和数据一致性问题。恶意用户可能利用自动创建索引进行恶意操作,而且多个节点同时创建索引可能导致数据不一致。同时,自动生成的 Mapping 可能不符合业务需求,需要后续进行调整。
通过深入理解 ElasticSearch 的自动创建索引机制,开发人员和运维人员可以更好地利用这一功能,同时避免其带来的潜在问题,从而构建高效、稳定的 ElasticSearch 应用。在实际应用中,应根据具体的业务场景和需求,合理配置自动创建索引的相关参数,确保 ElasticSearch 集群的性能和数据的安全性。