国瑾瑶 发表于 2025-6-21 12:26:52

浅谈ElasticSearch索引

概述

官方文档:https://www.elastic.co/docs/manage-data/data-store/index-basics
索引是Elasticsearch中的基本存储单元,类似于MySql数据库中的表,包含一堆有相似结构的文档数据,在Elasticsearch可以创建多个索引。
tips:
每个索引默认有5个主分片,1个副本分片,其中的主分片和副本分片的作用,我们下面讲解。
添加至索引中的数据,会被近实时(1秒内)的被搜索(这里不是立即可以被检索)
近实时被搜索如何理解?下面我们会仔细讲解,参考文档:https://www.elastic.co/docs/manage-data/data-store/near-real-time-search
索引的组成

逻辑层面

从逻辑层面来看,索引是由下面几个部分组成:

[*]分片

[*]索引的逻辑拆分单元,每个分片是一个独立的 Lucene 索引
[*]主分片(Primary Shards)负责数据写入和读取,副本分片(Replica Shards)用于容错和负载均衡
[*]分片数量在索引创建时指定,默认 5 个主分片,1个副本分片,可通过设置调整

设置分片示例
PUT /my_index
{
"settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
}
}

[*]映射(Mapping)

[*]定义索引中字段,字段的类型、分词器、存储方式等元数据
[*]支持动态映射(自动推断字段类型)和手动映射(精确控制字段行为)

映射示例:
{
"mappings": {
    "properties": {
      "title": { "type": "text", "analyzer": "ik_max_word" },
      "tags": { "type": "keyword" },
      "create_time": { "type": "date" }
    }
}
}

[*]文档(Document)

[*]索引中真实的数据记录,类似mysql表中的行数据
[*]索引的最小数据单元,以 JSON 格式存储,包含多个字段(Fields)
[*]每个文档有唯一标识符(ID),可自动生成或手动指定

示例文档记录
{
"_index": "my-first-elasticsearch-index",
"_id": "DyFpo5EBxE8fzbb95DOa",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
    "email": "john@smith.com",
    "first_name": "John",
    "last_name": "Smith",
    "info": {
      "bio": "Eco-warrior and defender of the weak",
      "age": 25,
      "interests": [
      "dolphins",
      "whales"
      ]
    },
    "join_date": "2024/05/01"
}
}物理层面

ES 索引的底层基于 Lucene,其物理存储由以下部分构成:

[*]Lucene 分段(Segments)

[*]索引的物理存储单元,每个分段是一个独立的倒排索引
[*]分段由 Refresh 操作生成,包含文档的索引数据和元数据
[*]分段特性:

[*]不可变(Immutable),生成后无法修改,保证搜索时的数据一致性
[*]后台自动合并(Merge),减少分段数量,优化搜索性能


[*]倒排索引(Inverted Index)

[*]Lucene 的核心数据结构,是 ES 快速检索的基础
[*]倒排索引将 “文档 - 词语” 的正向关系转换为 “词语 - 文档列表” 的反向关系

结构示例:
词语(Term) | 文档ID列表(Postings List)
-----------|---------------------------
Elasticsearch |
搜索引擎   |
分布式       |

[*]索引文件(Index Files)

[*]每个 Lucene 分段包含多个索引文件,常见类型:

[*]*.nvd:存储文档字段值
[*]*.nvm:字段值的元数据
[*]*.tim:记录词语(Term)的字典和频率
[*]*.doc:文档偏移量索引
[*]*.pos:词语在文档中的位置信息(用于短语搜索)
[*]*.dim:分段元数据(如创建时间、版本等)


[*]事务日志(Translog)

[*]记录所有未持久化的索引操作,确保数据不丢失
[*]存储路径:data//nodes//indices//translog
[*]作用:

[*]保障 ES 重启后数据恢复
[*]支持增量恢复(Incremental Recovery)


索引分片的作用

主分片

主分片是 Elasticsearch(ES)索引数据存储的基础单元。也是实现ES分布式高可用的基石,主分片在创建索引时通过number_of_shards指定,默认是5个主分片,主分片的数量可以根据主节点的数量来进行设置。
常规业务场景下(数据量中等,单分片≤50GB),每个节点的分片可以设置成1-2个,例如你的主节点有3个,在每个节点的资源规格都一样的情况下,那么你的主分片可以设置为3个或者6个
大数据量场景下(单分片 50GB100GB,总数据量≤600GB),每个节点的分片可以设置成34个,例如你的主节点有3个,在每个节点的资源规格都一样的情况下,那么你的主分片可以设置为9个或者12个
主分片经过设置之后是不可更改的,所以在设置主分片时需要经过慎重的考虑。
其主要作用有以下几点:

[*]每个索引被拆分为多个主分片(默认 5 个),每个主分片是一个独立的 Lucene 索引,负责存储索引的部分文档数据,当执行搜索、更新、删除等操作时,请求会被分发到相关主分片上执行,确保数据操作的直接性和高效性。
[*]主分片是数据写入的唯一入口:所有文档的创建、更新、删除操作都首先在主分片上完成,再同步到副本分片,主分片维护着文档的最新版本和元数据(如版本号、路由信息),确保集群内数据的一致性。
[*]通过增加主分片数量(创建索引时指定),可以将数据分散到更多节点,提升集群的存储容量和并发处理能力。每个主分片可独立分配到不同节点,实现负载均衡(如通过 ES 的分片分配机制自动调整)。
副本分片

副本分片是主分片的镜像拷贝,主要为集群提供可靠性、可用性和性能优化能力。副本分片占用与主分片相同的存储空间,增加副本会提高内存和磁盘使用量,副本分片在创建索引时通过number_of_replicas指定,默认值是1。
副本分片设置之后是可以修改的,如下:
PUT /your_index/_settings
{
"number_of_replicas": 2// 设置副本数为2
}副本分片的作用如下:

[*]当某个节点故障或主分片所在节点不可用时,副本分片会被提升为新的主分片(通过 ES 的自动故障转移机制),确保服务不中断,每个副本分片与主分片分布在不同节点(默认策略),避免单点故障(如节点硬件故障、网络分区)导致数据丢失
[*]副本分片可处理读请求(如搜索、获取文档),分担主分片的查询压力,尤其在高并发场景下显著提升集群吞吐量,同一索引的副本分片越多,可并行处理的读请求越多(如每个副本分片可独立响应查询)。
[*]副本分片是数据的冗余存储,即使部分节点损坏,仍可通过其他节点的副本恢复完整数据(无需依赖外部备份),可通过调整副本数量(默认 1 个)平衡可用性和资源消耗(每个副本占用与主分片相同的存储空间)。
修改副本分片的注意事项


[*]注意资源消耗

[*]每个副本分片占用与主分片相同的存储空间,增加副本会提高内存和磁盘使用量。
[*]例如5 主分片 ×2 副本 = 15 个分片,需预留相应存储和 JVM 堆内存。

[*]注意性能影响

[*]增加副本可提升读性能(更多分片可并行处理查询),但修改期间会产生网络流量(复制数据),可能短暂影响集群性能。
[*]建议在低峰期执行大规模副本调整。

[*]故障转移能力

[*]至少保留 1 个副本以保证高可用(允许 1 个节点故障)。
[*]若需容忍 N 个节点故障,副本数应≥N。

[*]集群状态监控

[*]修改过程中通过GET /_cluster/health监控集群状态,确保status为green或yellow(red表示有未分配的主分片)

副本分片和主分片的数量对应关系

当主分片设置成3,副本分片设置成1时,那么一共会有6个分片存在集群中,副本分片的1是将对应的3个主分片分别copy一份在不同的节点中。

索引的近实时性

官方文档:https://www.elastic.co/docs/manage-data/data-store/near-real-time-search
近实时性是指当数据写入至ES的索引中,需要一定时间(官方描述的是1秒内)才能被检索到,这个可以通过下面的参数设置
# 索引创建时设置refresh_interval为500ms
PUT /my_index
{
"settings": {
    "refresh_interval": "500ms" # 默认1000ms,也就是1秒
}
}refresh_interval参数并不是设置的越小越好,当值为0时,会禁用自动 Refresh,仍需手动触发 Refresh 才能让文档可搜索,这会引入人为延迟(人为操作,延迟可能更大)。
Refesh机制

Refresh 机制是控制近实时性的关键

[*]默认 Refresh 间隔:ES 默认每 1 秒执行一次 Refresh 操作,这就是 “1 秒近实时” 的来源
[*]手动触发 Refresh:可通过 API 强制刷新,实现更低延迟(如POST /index/_refresh)
[*]性能与实时性的权衡:缩短 Refresh 间隔会提高实时性,但会增加 IO 开销;延长间隔则反之
为什么ES不是 “完全实时”的?


[*]Refresh 间隔的存在:即使将refresh_interval设为 0(禁用自动 Refresh),仍需手动触发 Refresh 才能让文档可搜索,这会引入人为延迟。
[*]写入流程的固有延迟:文档从写入到生成分段需要经历内存缓冲区处理,无法做到完全实时(如数据库的实时查询)。
[*]设计权衡:ES 牺牲极小的实时性(1 秒)换取高性能和分布式架构的稳定性,这是分布式搜索系统的典型设计。
索引的管理

ES的索引类似类似数据库中的表,管理ES的索引需要通过ES的API来进行管理。索引的API主要有以下
创建索引

API

PUT /{index}
{
"aliases": { ... },# 指定索引别名
"settings": { ... }, # 指定索引的设置
"mappings": { ... }# 指定映射
}参数说明:
index:索引的名称,需要遵循以下规则:

[*]仅限小写字母
[*]不能包含\、/、 *、?、"、、|、#以及空格符等特殊符号
[*]从7.0版本开始不再包含冒号
[*]不能以-、_或+开头
[*]不能超过255个字节(注意它是字节,因此多字节字符将计入255个限制)
aliases:索引别名(Aliases) 是指向一个或多个索引的逻辑名称,可用于简化查询、实现读写分离或索引滚动等场景
settings:索引的配置参数,如分片数、副本数、刷新间隔等。常用的配置参数为:

[*]number_of_shards:指定索引分片
[*]number_of_replicas:指定副本数
[*]refresh_interval:指定刷新时间,单位秒
[*]auto_expand_replicas:自动调整副本数(根据节点数)
[*]indices.memory.index_buffer_size:索引缓冲区占堆内存的比例(默认10%)
[*]index.translog.durability:异步刷新事务日志(默认sync)
[*]index.translog.flush_threshold_size:事务日志刷新阈值
[*]index.queries.cache.enabled:启用查询结果缓存(默认true)
[*]indices.queries.cache.size:缓存占堆内存比例
[*]indices.fielddata.cache.size:字段数据缓存大小
[*]index.priority:控制索引在恢复和分片分配时的优先级
[*]index.routing.allocation.include.tag:强制分片分配到特定节点,包含特定标签的节点
[*]index.routing.allocation.exclude._name:强制分片分配到特定节点,排除特定节点
[*]index.blocks.write:true设置索引只读
[*]index.hidden:true设置索引隐藏,不显示在_cat/indices
[*]index.max_result_window:调整深度分页限制,默认10000,慎用!推荐用scroll或search_after
mappings:定义索引字段的数据类型和索引方式。后续会单独讲解mappings
实战:创建第一个索引

可以进入kibana进行创建:

示例:
# 创建请求
PUT index_test01
{
"settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "refresh_interval": "1s"
}
}

# 预期返回
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "index_test01"
}查看索引

查看所有的索引

示例:
# 请求接口
GET /_cat/indices?v&s=index

# 预期返回
health status index                            uuid                   pri rep docs.count docs.deleted store.size pri.store.size
greenopen   .apm-agent-configuration         KC2krPV1Q6On8zLG-2Ap6g   1   1          0            0       454b         227b
greenopen   .apm-custom-link               fW3LcrktTOGTDKvglhgODQ   1   1          0            0       454b         227b
greenopen   .kibana_7.17.26_001            Bv6ZMZL6STaho_Phwbrv1w   1   1      691         16      5.3mb          2.8mb
greenopen   .kibana_task_manager_7.17.26_001 0wrV35RzTWahe5vAUJKVow   1   1         17      25815      5.8mb          2.8mb
greenopen   index_test01                     TRTmGpiNRuC1wZY49Wt_aA   3   1          0            0      1.3kb         681b返回字段说明:

[*]health:索引的健康程度,green表示所有主分片和副本分片都正常运行,red表示至少有一个主分片未分配(数据可能丢失),yellow表示主分片正常,但至少有一个副本分片未分配
[*]status:open表示索引可正常读写,close表示索引已关闭,不可访问
[*]index:索引名称,以 . 开头的通常是系统索引,如 Kibana、APM 等组件创建的索引
[*]uuid:索引的唯一标识符(用于内部识别,不可修改)
[*]pri:主分片数量(创建索引时指定,不可动态修改)
[*]rep:主分片的副本数量(可动态调整)
[*]docs.count:索引中的文档总数
[*]docs.deleted:已标记删除但尚未物理删除的文档数(段合并后会清理)
[*]store.size:索引的总存储大小(包括主分片和所有副本)
[*]pri.store.size:主分片的存储大小(不包括副本)
查看指定的索引

api:GET /{index-name}
示例:
# 请求
GET /index_test01

# 预期返回
{
"index_test01" : {
    "aliases" : { },
    "mappings" : { },
    "settings" : {
      "index" : {
      "routing" : {
          "allocation" : {
            "include" : {
            "_tier_preference" : "data_content"
            }
          }
      },
      "refresh_interval" : "1s",         # 刷新时间
      "number_of_shards" : "3",          # 主分片数量
      "provided_name" : "index_test01",# 索引名称
      "creation_date" : "1750482108424", # 创建时间
      "number_of_replicas" : "1",      # 副本数量
      "uuid" : "TRTmGpiNRuC1wZY49Wt_aA", # uuid,唯一标识符
      "version" : {
          "created" : "7172699"
      }
      }
    }
}
}其它查询的api

API 路径功能描述示例GET /{index}/_settings获取索引设置GET /products/_settingsGET /{index}/_mapping获取索引映射GET /products/_mappingGET /{index}/_alias获取索引别名GET /products/_aliasGET /{index}/_stats获取索引统计信息GET /products/_statsGET /{index}/_shards获取分片状态GET /products/_shardsGET /_cluster/health集群健康状态GET /_cluster/healthGET /_cluster/health/{index}特定索引健康状态GET /_cluster/health/productsGET /{index}/_recovery恢复进度GET /products/_recoveryGET /{index}/_segments段信息GET /products/_segmentsGET /{index}/_field_usage_stats字段使用统计GET /products/_field_usage_stats修改索引

在 Elasticsearch 中,修改索引设置的 API 允许动态调整索引的配置参数(如副本数、刷新间隔等)
动态修改索引设置(运行时生效)

修改单个或多个设置
示例:
PUT /{index}/_settings
{
"settings": {
    "setting_name_1": "value_1",
    "setting_name_2": "value_2",
    ...
}
}修改副本数
# 请求
PUT /index_test01/_settings
{
"settings": {
    "number_of_replicas": 2
}
}

# 预期返回
{
"acknowledged" : true
}

# 查看一下
GET /index_test01/_settings
{
"index_test01" : {
    "settings" : {
      "index" : {
      "routing" : {
          "allocation" : {
            "include" : {
            "_tier_preference" : "data_content"
            }
          }
      },
      "refresh_interval" : "1s",
      "number_of_shards" : "3",
      "provided_name" : "index_test01",
      "creation_date" : "1750482108424",
      "number_of_replicas" : "2",
      "uuid" : "TRTmGpiNRuC1wZY49Wt_aA",
      "version" : {
          "created" : "7172699"
      }
      }
    }
}
}支持动态修改的参数

设置名称说明示例值number_of_replicas副本数2refresh_interval刷新间隔(控制新文档可见性)30s, -1(禁用自动刷新)index.search.slowlog.threshold.query.warn查询慢日志阈值(警告级别)10sindex.max_result_window分页最大结果数(深度分页风险)100000index.blocks.write禁止写入(只读模式)trueindex.routing.allocation.exclude._name排除特定节点node1,node2index.priority恢复优先级10静态设置参数(需关闭索引后修改)

某些设置(如主分片数)必须在索引关闭后才能修改:
# 关闭索引
POST /{index}/_close

# 修改静态设置
PUT /{index}/_settings
{
"settings": {
    "number_of_shards": 10# 仅示例,生产环境谨慎修改
}
}删除索引(谨慎操作,可以使用关闭索引替代)

在 Elasticsearch 中,删除索引是一个不可逆操作,会永久移除索引及其所有数据
删除单个索引

api:DELETE /{index}
示例:
# 请求
DELETE /index_test01

# 预期返回
{
"acknowledged" : true
}

# 验证索引是否存在
HEAD /index_test01
# 预期返回,404表示已经删除
{"statusCode":404,"error":"Not Found","message":"404 - Not Found"}删除多个索引

使用逗号分隔多个索引名,或通配符匹配:
示例:
DELETE /{index1},{index2},...

# 通配符,删除所有以logs-开头的索引
DELETE /logs-*删除所有索引(谨慎操作)

警告:此操作会删除集群中所有索引,生产环境中禁用!
安全措施:部分集群配置了action.destructive_requires_name为true,禁止通过通配符删除所有索引。
DELETE /_all
DELETE /*索引的其它操作


[*]关闭索引:POST /{index}/_close,关闭索引,使其不能进行读写操作,以节省资源。
[*]打开索引:POST /{index}/_open,打开已关闭的索引,恢复其读写功能。
[*]收缩索引:POST /{index}/_shrink/{target_index},将索引收缩为指定的目标索引,可减少分片数量。
[*]拆分索引:POST /{index}/_split/{target_index},将索引拆分为多个新的索引。
[*]克隆索引:POST /{index}/_clone/{target_index},克隆一个现有的索引到新的索引。
[*]索引滚动:POST /{alias}/_rollover,用于将数据从一个索引滚动到另一个索引,常用于按时间滚动的索引场景。
[*]冻结索引:POST /{index}/_freeze,冻结索引,使其数据不可变,以节省内存和提高查询性能。
[*]解冻索引:POST /{index}/_unfreeze,解冻已冻结的索引,恢复其可写状态。
[*]解析索引:GET /_resolve_index/{index},解析索引的名称,返回其真实的索引名称和相关信息。
[*]检查索引是否存在:HEAD /{index},通过请求的响应状态码判断索引是否存在,200 表示存在,404 表示不存在。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 浅谈ElasticSearch索引