概述
官方文档: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": "[email protected]",
"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 | [1, 3, 5]
搜索引擎 | [1, 2, 4]
分布式 | [1, 3, 4]
-
索引文件(Index Files)
- 每个 Lucene 分段包含多个索引文件,常见类型:
- *.nvd:存储文档字段值
- *.nvm:字段值的元数据
- *.tim:记录词语(Term)的字典和频率
- *.doc:文档偏移量索引
- *.pos:词语在文档中的位置信息(用于短语搜索)
- *.dim:分段元数据(如创建时间、版本等)
- 每个 Lucene 分段包含多个索引文件,常见类型:
-
事务日志(Translog)
- 记录所有未持久化的索引操作,确保数据不丢失
- 存储路径:
data/<cluster_name>/nodes/<node_id>/indices/<index_uuid>/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
green open .apm-agent-configuration KC2krPV1Q6On8zLG-2Ap6g 1 1 0 0 454b 227b
green open .apm-custom-link fW3LcrktTOGTDKvglhgODQ 1 1 0 0 454b 227b
green open .kibana_7.17.26_001 Bv6ZMZL6STaho_Phwbrv1w 1 1 691 16 5.3mb 2.8mb
green open .kibana_task_manager_7.17.26_001 0wrV35RzTWahe5vAUJKVow 1 1 17 25815 5.8mb 2.8mb
green open 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/_settings |
GET /{index}/_mapping |
获取索引映射 | GET /products/_mapping |
GET /{index}/_alias |
获取索引别名 | GET /products/_alias |
GET /{index}/_stats |
获取索引统计信息 | GET /products/_stats |
GET /{index}/_shards |
获取分片状态 | GET /products/_shards |
GET /_cluster/health |
集群健康状态 | GET /_cluster/health |
GET /_cluster/health/{index} |
特定索引健康状态 | GET /_cluster/health/products |
GET /{index}/_recovery |
恢复进度 | GET /products/_recovery |
GET /{index}/_segments |
段信息 | GET /products/_segments |
GET /{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 | 副本数 | 2 |
refresh_interval | 刷新间隔(控制新文档可见性) | 30s, -1(禁用自动刷新) |
index.search.slowlog.threshold.query.warn | 查询慢日志阈值(警告级别) | 10s |
index.max_result_window | 分页最大结果数(深度分页风险) | 100000 |
index.blocks.write | 禁止写入(只读模式) | true |
index.routing.allocation.exclude._name | 排除特定节点 | node1,node2 |
index.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 表示不存在。