Elasticsearch 从入门到精通:术语、索引、分片、读写流程与面试高频题一文搞懂
1. 核心术语
术语 | 含义说明 |
---|---|
Index(索引) | 是数据存储的逻辑单元,类似于数据库中的“表”。用户将数据写入某个 Index,每个 Index 可以被划分为多个分片。在 Elasticsearch 7.x 之后,默认每个索引只有一个主分片类型(_doc ),且不再支持多类型(types)。 |
Shard(分片) | 将一个 Index 的数据拆分成多个部分,实现分布式存储。一个 Index 至少要有一个主分片。每个分片是一个独立的搜索引擎实例(Lucene 实例)。 |
Primary Shard(主分片) | 每个文档必须属于一个主分片。主分片负责处理所有的写操作,并将数据变更同步到副本分片。 |
Replica Shard(副本分片) | 主分片的拷贝,提供数据冗余、高可用和读负载均衡。副本分片仅处理读请求(如 Get、Search),不接受写入。 |
Document(文档) | 实际存储的数据,是 JSON 格式的对象,是 ES 中最小的数据单元。每个文档有唯一 _id ,并归属于某个 _index 。 |
2. 分片详解
✅ 主分片(Primary Shard)
- 每个文档必须属于一个主分片。
- 数据写入时,先写入主分片,再同步到副本。
- 数量在创建索引时指定,不可更改。
📌 分片分配原则:
文档通过哈希算法决定写入哪个主分片:
shard_num = hash(_routing) % number_of_primary_shards
其中 _routing
默认为文档的 _id
,也可自定义(如 user_id
)。
⚠️ 一旦主分片数确定,后续无法修改,否则哈希结果变化会导致数据无法定位。
✅ 副本分片(Replica Shard)
- 提供高可用性和负载均衡。
- 可随时增加或减少副本数量(通过
_settings
API)。 - 如果主分片损坏,副本可被提升为新的主分片。
⚠️ 注意:
- 一个分片默认有
0
个副本。- 推荐生产环境至少设置
1
个副本,防止节点故障导致数据丢失。
3. 集群状态颜色(Cluster Health)
颜色 | 状态 | 含义 |
---|---|---|
Green(绿色) | ✅ 健康 | 所有主分片和副本分片都正常工作。 |
Yellow(黄色) | ⚠️ 亚健康 | 所有主分片正常,但部分副本分片异常或未分配。可能导致数据丢失风险(无副本保护)。 |
Red(红色) | ❌ 不健康 | 有主分片无法正常访问,部分数据不可用。 |
4. 高可用与容错机制(推荐奇数节点)
Elasticsearch 集群依赖多数派(Quorum)机制选举主节点(Master Node),确保集群元数据一致性和可用性。
容忍 N 台机器故障,至少需要
2N + 1
个主候选节点(master-eligible nodes)
✅ 容错能力对照表
节点总数 | 最大可容忍故障数 | 说明 |
---|---|---|
1 | 0 | 单点故障,不推荐 |
2 | 0 | 平票,无法形成多数派 |
3 | 1 | 推荐最小高可用集群 |
5 | 2 | 生产常用规模 |
6 | 2 | 成本高,无优势 |
7 | 3 | 大型集群常用 |
✅ 为什么推荐奇数节点?
- 避免脑裂(Split Brain):奇数节点更容易形成多数派(>50%),避免两个分区各自认为自己是主集群。
- 成本最优:5 节点和 6 节点容错能力相同(最多容忍 2 台故障),但 6 节点成本更高。
- 运维简单:奇数节点更直观,便于规划。
📌 最佳实践:
- 生产环境建议部署 3 或 5 个专用主候选节点(
node.roles: [master]
)。- 数据节点可为偶数,但主候选节点组应为奇数。
- 正确配置
discovery.seed_hosts
和cluster.initial_master_nodes
。
5. 文档写入流程(Indexing 流程)
当你使用 PUT /index/_doc/1
插入文档时,Elasticsearch 经历以下步骤:
✅ 1. 客户端请求
客户端发送请求到任意节点(该节点成为“协调节点”)。
PUT /my-index/_doc/1
{"name": "Tom","age": 25
}
✅ 2. 路由(Routing)
协调节点根据文档的 _id
(或自定义路由字段)决定该文档属于哪个 主分片(Primary Shard)。
- Elasticsearch 默认使用公式:
shard_num = hash(_id) % primary_shard_count
✅ 3. 写入主分片
协调节点将请求转发给主分片所在节点:
- 写入 Translog(事务日志) → 保证持久化(可配置是否立即刷盘)
- 更新 In-Memory Buffer → 缓存待索引文档
- (可选)通过
refresh=true
强制刷新,使文档可搜索
默认每秒执行一次
refresh
,将 Buffer 中的文档刷新到 倒排索引(Lucene Segment),使其可搜索。
✅ 4. 同步副本分片
主分片写入成功后,协调节点将请求同步复制到所有副本分片节点(默认同步等待):
- 副本节点同样执行:写 Translog + 更新 Buffer
- 副本也会定期
refresh
,但不保证与主分片完全同步
可通过
?replication=async
改为异步复制(不推荐,降低一致性)
✅ 5. 返回成功响应
当主分片和 足够数量的副本分片(由 write consistency
决定)都确认写入后,协调节点返回成功。
consistency
可设为:one
(主分片)、quorum
(多数)、all
(全部副本)
这是一个非常关键的问题!Elasticsearch 并不是必须“所有副本写入成功”才算整体写入成功。是否成功,取决于你设置的 写一致性级别(Write Consistency),也叫 写关注(Write Concern)。
✅ 6.写入成功的判定标准:由 consistency
参数决定
当你执行一个写操作(如 PUT /index/_doc/1
),可以通过 URL 参数指定 consistency
,它决定了需要多少个分片副本确认写入,协调节点才向客户端返回成功。
三个可选值:
值(consistency ) | 含义说明 |
---|---|
one | 只要主分片写入成功即可返回。最快,但最不安全(副本可能未同步,主分片宕机则数据丢失)。 |
quorum (默认) | 必须有多数派(quorum) 的分片写入成功。这是默认且最常用的设置。 |
all | 必须所有分片(主分片 + 所有副本分片)都写入成功才返回。最安全,但最慢,任何一个副本失败都会导致写入失败。 |
✅ quorum
(多数派)是如何计算的?
quorum = int((primary + replica) / 2) + 1
也就是说:超过一半的分片必须成功。
📌 举几个例子:
主分片数 | 副本数 | 总分片数(主+副) | quorum 要求成功数 | 写入成功的最小成功数 |
---|---|---|---|---|
1 | 1 | 2(1主+1副) | (1+1)/2 + 1 = 2 | 2(主+副都成功) |
1 | 2 | 3(1主+2副) | (1+2)/2 + 1 = 2 | 2(主+至少1个副) |
3 | 1 | 6(3主+3副) | (3+3)/2 + 1 = 4 | 4(每个主分片组至少2个成功) |
💡 关键点:Elasticsearch 是按 每个主分片及其副本组 来计算
quorum
的,而不是整个索引。
✅ 举个实际例子
假设你有一个索引:
number_of_primary_shards: 1
number_of_replicas: 2
(即有 2 个副本分片)
那么总共有 3 个分片(1主 + 2副)。
quorum = (1 + 2) / 2 + 1 = 2.5 → 取整为 2?
❌- 正确计算:
quorum = int((primary_count + replica_count) / 2) + 1 = int(3/2) + 1 = 1 + 1 = 2
所以,只要 主分片 + 至少一个副本分片 写入成功,协调节点就会返回成功。
✅ 即使有一个副本分片失败或延迟,写入仍然成功。
✅ 总结:写入多少才算成功?
场景 | 成功条件 |
---|---|
consistency=one | 主分片成功即可 |
consistency=quorum (默认) | 主分片 + 多数派副本成功(即 int((primary+replica)/2) + 1 ) |
consistency=all | 主分片 + 所有副本都必须成功 |
📌 生产环境建议
- 绝大多数场景使用默认
quorum
:在性能、可用性和数据安全之间取得了最佳平衡。 - 高安全场景(如金融交易日志):可考虑
all
,但要接受性能下降和更高的失败率。 - 高吞吐写入场景(如日志采集):可使用
one
+ 异步处理,但需接受一定的数据丢失风险。
🔍 附加说明:replication=async
参数
你提到的 ?replication=async
是旧版本(v1.x)的参数,在新版本中已被废弃。
现在的复制行为由 consistency
和集群配置决定,复制本身在 quorum
模式下是同步阻塞的(协调节点会等待足够数量的副本响应),但这个“同步”是为了保证一致性,并不意味着所有副本必须同时完成。
✅ 一句话概括:
写入成功 = 主分片成功 + 满足
consistency
规则的副本数量成功,不一定要所有副本都成功。默认quorum
模式下,只要“多数派”成功即可。
6. 文档读取流程(Search / Get 流程)
Elasticsearch 支持两种读取方式:
- Get API:根据文档 ID 获取单个文档(如
GET /index/_doc/1
) - Search API:根据条件搜索多个文档(如
POST /index/_search
)
✅ 1. Get API 流程(根据 ID 查询)
GET /my-index/_doc/1
步骤:
- 协调节点接收请求
- 根据
_id
计算目标分片(主或副本) - 使用轮询策略选择一个可用分片节点(实现负载均衡)
- 分片节点从 Lucene 读取文档(默认实时查询,合并内存更新)
- 返回结果
✅ 优势:Get 是 实时(real-time) 操作,即使文档刚写入未 refresh 也能读到。
✅ 2. Search API 流程(搜索查询)
POST /my-index/_search
{"query": { "match_all": {} }
}
分为两个阶段:
阶段一:查询(Query Phase)
- 协调节点将查询广播到索引的每个分片(主或副本)
- 每个分片本地执行查询,返回 Top N 文档 ID + 分数
- 协调节点合并结果,进行全局排序、分页
阶段二:取回(Fetch Phase)
- 协调节点根据排序后的文档ID,向包含这些文档的分片发送请求
- 分片返回完整文档内容
- 协调节点组装最终结果并返回
⚠️ 深分页(如
from=10000
)性能差,建议使用search_after
或scroll
。
7. 关键概念补充
概念 | 说明 |
---|---|
Translog(事务日志) | 写入时先写日志,用于故障恢复和 refresh 前的数据持久化。可通过 index.translog.durability 控制刷盘策略。 |
Refresh | 将内存 Buffer 内容刷新到 Lucene Segment,使其可搜索。默认 1s 一次,可配置 refresh_interval 。 |
Flush | 将内存 Buffer 和 Translog 写入磁盘,生成新的 Segment 并清空 Translog。默认 30min 或 Translog 过大时触发。 |
副本一致性(Consistency) | 控制写入需多少分片确认:one / quorum (默认)/ all 。 |
副本读取(Replica Read) | Get/Search 可从副本读取,实现读负载均衡。协调节点自动轮询选择。 |
8. 总结:写流程 vs 读流程
阶段 | 写流程(Indexing) | 读流程(Search/Get) |
---|---|---|
定位分片 | 通过 _id 路由到主分片 | 通过 _id 或广播到所有分片 |
主分片操作 | 写 Translog + Buffer | 从 Lucene 读取 |
副本操作 | 同步复制(默认 quorum ) | 可从副本读取(负载均衡) |
响应确认 | 主分片 + 足够副本成功 | 返回查询结果 |
实时性 | 默认异步(1s refresh),可 refresh=true 强制 | Get 实时,Search 依赖 refresh 间隔 |
9. 面试题彩蛋(高频考点)
🔹 Q1: ES 集群的 9200 和 9300 端口分别有什么作用?
端口 | 协议 | 用途 |
---|---|---|
9200 | HTTP | RESTful API 端口,用于数据操作(CRUD)、集群管理等。 |
9300 | TCP | 节点间通信端口,用于集群内部节点之间的交互(如发现、选举、数据传输等),通常用于 Java 客户端连接。 |
🔹 Q2: ES 集群的几种颜色代表什么含义?
已在上文“集群状态颜色”中详细说明。
🔹 Q3: 主分片和副本分片的区别是什么?
对比项 | 主分片(Primary Shard) | 副本分片(Replica Shard) |
---|---|---|
写操作 | ✅ 支持 | ❌ 不支持 |
读操作 | ✅ 支持 | ✅ 支持 |
数据来源 | 原始写入数据 | 来自主分片的复制 |
数量限制 | 创建时指定,不能修改 | 可动态调整 |
故障恢复 | 不可替代 | 可升级为新的主分片 |
🔹 Q4: 如何理解 Elasticsearch 中文档的“不可变”特性?
- 文档一旦写入主分片,就不可直接修改。
- 更新操作实际上是:删除旧文档 + 插入新版本文档。
- 这是为了保证副本的一致性与性能优化。文档写入后,底层 Lucene 不允许修改,只能标记删除。更新操作本质是:先标记旧文档为“已删除”,再写入新版本文档。删除和更新操作会在后续的 Segment Merge 过程中被物理清理。
10. 温馨提示 / 最佳实践
- 📌 一个索引最少要有 一个主分片。
- 📌 建议生产环境至少设置
1
个副本,提高可用性。 - 📌 分片数创建后 不能更改,需根据数据量预估(如每分片 30-50GB)。
- 📌 副本数可动态调整:
PUT /index/_settings { "number_of_replicas": 2 }
- 📌 避免过多分片(如 > 1000),会增加集群开销。
- 📌 使用
_cat/health
、_cat/shards
监控集群状态。 - 📌 对于日志类场景,可关闭自动 refresh(
"refresh_interval": -1
)以提升写入性能。