MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

ElasticSearch创建索引API详解

2023-02-053.5k 阅读

ElasticSearch创建索引基础概念

在深入了解ElasticSearch创建索引API之前,我们需要先明确一些基础概念。ElasticSearch是一个分布式的开源搜索引擎,以文档为基本存储单元,而索引则是文档的集合,类似于关系型数据库中的数据库概念。一个索引可以包含多个类型(在ElasticSearch 7.0+ 中,类型的概念逐渐被弱化并最终废弃),每个类型下又可以有多个文档。

从存储角度来看,索引被划分成多个分片(shard),每个分片可以有多个副本(replica)。分片是数据存储和检索的基本单元,副本则用于提高数据的可用性和容错性。

创建索引API的基本语法

在ElasticSearch中,创建索引使用PUT请求,基本语法如下:

PUT /{index}
{
    "settings" : {
        "index" : {
            "number_of_shards" : 3,
            "number_of_replicas" : 2
        }
    }
}

在上述示例中,{index}是你要创建的索引名称。settings部分用于定义索引的设置,其中number_of_shards指定了索引的主分片数量,number_of_replicas指定了每个主分片的副本数量。

索引名称规则

  1. 字符限制:索引名称只能包含小写字母、数字、下划线、连字符,且长度不能超过255个字符。
  2. 不能包含特殊字符:不能包含空格、逗号、冒号、斜杠等特殊字符,因为这些字符在ElasticSearch的URL路径中有特殊含义。例如,my_index是合法的索引名称,而my indexmy,index都是不合法的。

索引设置详解

  1. 分片和副本设置

    • 主分片(number_of_shards):主分片的数量在索引创建时就固定下来,后续不能轻易更改。它决定了数据在集群中的分布方式。一般来说,主分片数量应根据数据量和集群规模来合理设置。如果数据量较小,设置过多的主分片会增加管理开销;如果数据量较大,主分片数量不足可能导致单个分片数据量过大,影响查询性能。例如,对于一个数据量预计在10GB左右的索引,设置3 - 5个主分片可能比较合适。
    • 副本(number_of_replicas):副本数量可以在索引创建后动态调整。副本的主要作用是提高查询性能和数据可用性。当某个主分片所在的节点出现故障时,副本分片可以接替其工作。例如,将副本数量设置为2,意味着每个主分片都有两个副本,这样在某个节点故障时,数据仍然可以从其他副本获取。
  2. 索引存储类型设置

    • 存储引擎:ElasticSearch默认使用Lucene作为存储引擎。Lucene提供了高效的倒排索引结构,用于快速的文本检索。虽然目前ElasticSearch没有提供在创建索引时选择其他存储引擎的选项,但了解这一点有助于理解索引的底层原理。
    • 数据格式:ElasticSearch支持多种数据格式存储,如JSON。在创建索引时,虽然不需要显式指定数据格式,但所有文档数据最终都以JSON格式存储在索引中。
  3. 索引的动态映射设置

    • 动态映射(Dynamic Mapping):ElasticSearch具有动态映射功能,当文档被索引时,如果索引中不存在该文档字段的映射,ElasticSearch会自动根据文档内容推断字段的数据类型并创建映射。例如,如果你索引一个包含"age": 30的文档,ElasticSearch会自动将age字段映射为integer类型。
    • 动态映射控制:在创建索引时,可以通过dynamic参数来控制动态映射行为。dynamic参数有三个取值:true(默认值)表示自动添加新字段的映射;false表示忽略新字段,不添加映射;strict表示如果遇到新字段则抛出异常。例如:
PUT /my_index
{
    "settings" : {
        "index" : {
            "number_of_shards" : 1,
            "number_of_replicas" : 1
        }
    },
    "mappings" : {
        "dynamic" : "false",
        "properties" : {
            "name" : {
                "type" : "text"
            }
        }
    }
}

在上述示例中,dynamic设置为false,如果索引的文档中包含name以外的字段,这些字段将不会被索引。

创建索引的高级设置

  1. 索引别名设置
    • 别名(Alias):索引别名是给索引起的一个额外名称,它可以指向一个或多个索引。别名的主要作用是提供一种灵活的方式来管理索引,例如在索引重建或滚动更新时,通过别名可以保持客户端的查询不受影响。
    • 创建别名:在创建索引时可以同时创建别名,示例如下:
PUT /my_index
{
    "aliases" : {
        "my_alias" : {}
    },
    "settings" : {
        "index" : {
            "number_of_shards" : 1,
            "number_of_replicas" : 1
        }
    }
}

上述示例中,在创建my_index索引时,同时创建了一个名为my_alias的别名指向该索引。后续查询时,可以使用my_alias来代替my_index。 2. 索引模板设置 - 索引模板(Index Template):索引模板是一种预定义的配置,用于在创建索引时自动应用一些设置和映射。模板可以根据索引名称的模式来匹配,当创建的索引名称符合模板定义的模式时,模板中的设置和映射将被应用到该索引上。 - 创建索引模板:下面是一个创建索引模板的示例:

PUT _template/my_template
{
    "index_patterns" : ["my_index_*"],
    "settings" : {
        "index" : {
            "number_of_shards" : 2,
            "number_of_replicas" : 1
        }
    },
    "mappings" : {
        "properties" : {
            "timestamp" : {
                "type" : "date"
            }
        }
    }
}

在上述示例中,index_patterns指定了模板适用的索引名称模式为my_index_*。当创建如my_index_1my_index_2等符合该模式的索引时,模板中的设置(2个主分片,1个副本)和映射(timestamp字段为date类型)将自动应用到这些索引上。

基于不同客户端的创建索引代码示例

  1. 使用Elasticsearch Python客户端
    • 安装客户端:首先需要安装elasticsearch库,可以使用pip install elasticsearch命令进行安装。
    • 创建索引代码
from elasticsearch import Elasticsearch

es = Elasticsearch([{'host': 'localhost', 'port': 9200}])

index_name ='my_index'
body = {
  "settings" : {
    "index" : {
      "number_of_shards" : 1,
      "number_of_replicas" : 1
    }
  }
}

response = es.indices.create(index=index_name, body=body)
print(response)

在上述代码中,首先创建了一个Elasticsearch客户端实例,然后定义了索引名称和索引设置,最后使用indices.create方法创建索引,并打印响应结果。

  1. 使用Elasticsearch Java客户端
    • 添加依赖:在pom.xml文件中添加Elasticsearch客户端依赖:
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.10.2</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.10.2</version>
</dependency>
- **创建索引代码**:
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
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 CreateIndexExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("localhost", 9200, "http")));

        CreateIndexRequest request = new CreateIndexRequest("my_index");
        request.settings("index.number_of_shards", 1, "index.number_of_replicas", 1);

        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        System.out.println(response);

        client.close();
    }
}

上述Java代码中,创建了一个RestHighLevelClient客户端实例,构建了一个CreateIndexRequest请求,设置了索引的分片和副本数量,然后使用客户端执行创建索引的操作,并打印响应结果。

  1. 使用Elasticsearch JavaScript客户端
    • 安装客户端:使用npm install @elastic/elasticsearch命令安装Elasticsearch JavaScript客户端。
    • 创建索引代码
const { Client } = require('@elastic/elasticsearch')

const client = new Client({
    node: 'http://localhost:9200'
})

const indexName ='my_index'
const body = {
    settings: {
        index: {
            number_of_shards: 1,
            number_of_replicas: 1
        }
    }
}

client.indices.create({
    index: indexName,
    body: body
}).then(response => {
    console.log(response)
}).catch(error => {
    console.error(error)
})

在上述JavaScript代码中,创建了一个Elasticsearch客户端实例,定义了索引名称和索引设置,然后使用indices.create方法创建索引,并处理响应结果或错误。

创建索引时的常见问题及解决方法

  1. 索引名称冲突
    • 问题描述:当尝试创建一个已经存在的索引时,会抛出resource_already_exists_exception异常。
    • 解决方法:在创建索引之前,可以先检查索引是否存在。例如,在Python客户端中,可以使用es.indices.exists(index=index_name)方法来检查索引是否存在,如果存在则可以选择删除后重新创建或进行其他处理。
  2. 分片分配失败
    • 问题描述:在创建索引时,可能会出现分片分配失败的情况,通常是由于集群状态、节点故障或磁盘空间不足等原因导致。例如,当某个节点磁盘空间已满,ElasticSearch可能无法将分片分配到该节点上。
    • 解决方法:首先检查集群状态,使用GET /_cluster/health API查看集群是否处于健康状态。如果是磁盘空间问题,可以清理磁盘空间或调整分片分配策略。例如,可以通过设置cluster.routing.allocation.disk.threshold_enabledfalse来暂时禁用磁盘空间检查(不推荐在生产环境长期使用),或者将分片分配到其他有足够空间的节点上。
  3. 映射错误
    • 问题描述:在设置索引映射时,如果设置的字段类型与实际数据类型不匹配,可能会导致索引失败或数据查询异常。例如,将一个应该是integer类型的字段错误地映射为text类型,可能会导致数值查询出现问题。
    • 解决方法:仔细检查映射设置,确保字段类型与实际数据类型一致。可以使用PUT /{index}/_mapping API来更新索引的映射,但需要注意,部分映射更改(如字段类型更改)可能需要重建索引。

创建索引与集群健康关系

  1. 索引创建对集群健康的影响
    • 创建索引时:当创建一个新索引时,ElasticSearch需要为该索引分配分片和副本。这个过程会占用集群的资源,包括网络带宽、磁盘I/O和CPU等。如果在短时间内创建大量索引,可能会导致集群资源紧张,从而影响集群的健康状态。例如,集群状态可能会从green(健康)变为yellow(部分副本未分配)甚至red(存在未分配的主分片)。
    • 索引创建成功后:如果索引创建成功,并且所有分片和副本都正常分配,集群状态会恢复到green(假设没有其他问题)。每个索引的健康状态也可以通过GET /{index}/_health API查看,它会显示该索引的分片和副本的分配情况。
  2. 基于集群健康状态调整索引创建策略
    • 集群健康为green:可以正常创建索引,并且可以根据业务需求合理设置分片和副本数量。例如,如果业务对查询性能要求较高,可以适当增加副本数量。
    • 集群健康为yellow:虽然可以创建索引,但需要谨慎操作。此时应先排查导致副本未分配的原因,如节点故障、网络问题等,解决问题后再进行索引创建。如果必须在yellow状态下创建索引,应尽量减少对集群资源的占用,例如适当减少新索引的分片和副本数量。
    • 集群健康为red:不建议创建新索引,因为存在未分配的主分片意味着集群数据已经存在丢失风险。此时应优先解决导致主分片未分配的问题,如修复故障节点、调整分片分配策略等,待集群状态恢复到yellowgreen后再创建索引。

创建索引在不同场景下的优化策略

  1. 大数据量场景
    • 合理设置分片数量:对于大数据量场景,应根据数据量和集群规模来确定合适的主分片数量。一般原则是每个主分片的数据量控制在30GB - 50GB左右(这只是一个参考值,实际情况可能因硬件和业务需求而异)。例如,如果预计数据量为500GB,那么设置10 - 15个主分片可能比较合适。同时,需要考虑未来数据的增长趋势,预留一定的分片扩展空间。
    • 异步创建索引:为了避免创建索引过程对业务的影响,可以采用异步方式创建索引。在Elasticsearch Python客户端中,可以使用threading模块来实现异步操作。例如:
import threading
from elasticsearch import Elasticsearch

es = Elasticsearch([{'host': 'localhost', 'port': 9200}])

def create_index_async():
    index_name ='my_big_data_index'
    body = {
      "settings" : {
        "index" : {
          "number_of_shards" : 10,
          "number_of_replicas" : 2
        }
      }
    }
    es.indices.create(index=index_name, body=body)

thread = threading.Thread(target=create_index_async)
thread.start()
  1. 高并发写入场景
    • 调整副本数量:在高并发写入场景下,过多的副本会增加写入压力,因为每次写入都需要同步到副本分片。可以适当减少副本数量,例如将副本数量设置为1,待写入完成后再根据需要增加副本数量以提高数据可用性和查询性能。
    • 使用批量写入:为了提高写入效率,应尽量使用批量写入操作。在Elasticsearch Python客户端中,可以使用helpers.bulk方法。例如:
from elasticsearch import Elasticsearch
from elasticsearch.helpers import bulk

es = Elasticsearch([{'host': 'localhost', 'port': 9200}])

actions = [
    {
        "_index": "my_index",
        "_source": {
            "field1": "value1"
        }
    },
    {
        "_index": "my_index",
        "_source": {
            "field1": "value2"
        }
    }
]

bulk(es, actions)
  1. 多租户场景
    • 使用索引别名和模板:在多租户场景下,可以为每个租户创建独立的索引,但通过索引别名和模板来统一管理和配置。例如,创建一个索引模板,根据租户索引名称的模式应用相同的设置和映射。同时,使用别名来简化客户端的查询,客户端可以通过一个通用的别名来查询不同租户的数据,而无需关心具体的索引名称。
    • 权限控制:除了索引创建,还需要注意权限控制。Elasticsearch提供了基于角色的访问控制(RBAC),可以为不同租户分配不同的权限,确保租户之间的数据隔离。例如,租户A只能访问和操作自己的索引,无法访问租户B的索引。

通过深入理解ElasticSearch创建索引API及其相关的设置、优化策略和常见问题解决方法,开发人员可以更加高效地使用ElasticSearch,构建稳定、高性能的搜索应用。无论是在小型项目还是大规模的企业级应用中,合理创建索引都是确保ElasticSearch良好运行的关键一步。在实际应用中,需要根据具体的业务场景和需求,灵活运用这些知识,不断优化索引的创建和管理。