Elasticsearch 核心概念
Elasticsearch 核心概念
候选人小陈在字节面试时被问到:"你们项目里用 ES 做什么?索引结构是怎样的?"
小陈说:"我们建了一个订单索引,存了 2000 万订单数据。"
面试官追问:"你建了几个分片?副本怎么配的?为什么这么配?"
小陈愣了一下,说:"就默认的...5个分片,1个副本?"
面试官又问:"分片数和副本数是怎么算出来的?主分片和副本分片的区别是什么?"
小陈开始语无伦次,最后勉强说出"副本是用来做故障恢复的"。
【面试官心理】 我问他分片副本,不是想听他背定义。我想知道的是:他有没有经历过数据量增长后的容量规划,有没有踩过"分片过多导致 GC 压力大"或者"分片过少导致写入瓶颈"的坑。能说出"小分片多副本"还是"大分片少副本"这个 trade-off 的,基本都有生产经验。
一、Index / Type / Document 🔴
1.1 三层结构拆解
很多候选人把 ES 的数据和关系型数据库一一对应,这本身没错,但只对了一半:
但这里有个大坑:ES 的 Index 不是"数据库",它更像是"一个预建好索引的表"。你在创建 Index 的同时,ES 就在背后为你建好了倒排索引结构。
一个文档的结构大概长这样:
1.2 追问链
第一层:怎么存? 面试官问:"ES 里一个文档是怎么存储的?" 候选人答:"JSON 格式,存到 _source 字段里..." 考察点:基本 API 使用
第二层:底层结构 面试官追问:"那倒排索引是怎么建的?" 候选人答:"根据字段值建索引..."(可能卡在这里) 考察点:数据结构理解
第三层:分片与副本 面试官追问:"2000万数据你分了几个分片?为什么?副本怎么配的?" 候选人答:...(P5/P6 分水岭) 考察点:容量规划、生产经验
第四层:集群角色 面试官追问:"你的集群有哪些节点角色?Master 节点挂了会怎样?" 候选人答:...(P7 区分点) 考察点:架构理解、故障容灾
1.3 错误示范
候选人原话:"ES 的 Index 就相当于 MySQL 的数据库,Type 相当于表。"
问题诊断:
- 把 ES 和 MySQL 的映射关系想得太死
- 忽略了 Type 在 ES 7.x 已经被废弃的事实
- 不知道
_source和倒排索引是两回事
面试官内心 OS:"这个候选人可能用过 ES,但肯定没深入了解过它的底层结构。连 Type 废弃都不知道,说明他至少 3 年没更新过 ES 知识了。"
二、Shard 分片机制 🟡
2.1 主分片与副本分片
ES 的数据水平扩展靠的就是分片。每个 Index 创建时必须指定主分片数量(默认 5,ES 7.x 默认改为 1),副本分片数量可动态调整。
核心规则:
- 主分片数在 Index 创建后不可更改(这是最容易踩的坑)
- 副本分片数可以随时调整
- 主分片和副本不能同时存在于同一节点(否则该节点挂了副本也没了)
2.2 分片路由原理
文档写入时,ES 通过这个公式决定文档落到哪个分片:
默认 _routing 就是文档的 _id,你也可以指定自定义 routing 字段。
如果按 user_id 作为 routing key,所有相同用户的订单都会落在同一个分片内。这个技巧在"查询某个用户的所有订单"时非常有用,可以避免跨分片查询带来的 scatter-gather 开销。
2.3 分片数规划
这是面试中的高频深水区,也是 90% 候选人答不好的地方。
分片过小的风险:
- 单分片数据量过大,查询延迟飙升
- 分片恢复时间长(故障后拉取 100GB 数据 vs 10GB)
分片过大的风险:
- 分片过多导致元数据压力大(ES 集群状态由 Master 节点管理,每个分片都有元数据)
- 每次查询要扫描更多分片,GC 压力大
- Lucene 段合并成本增加
经验公式(针对 HDD):
- 每个分片数据量控制在 30GB~50GB 以内
- 每个节点的分片数控制在 20~30 个 以内
- 副本数通常设为 1~2,高可用要求高则设为 2
【面试官心理】 我问他分片规划,其实是在试探他有没有经历过"数据量从百万到千万"这个阶段。有这个经历的候选人,至少踩过一个坑:要么一开始分片设少了导致扩容,要么分片设多了导致集群抖动。
三、Segment 段存储 🟡
3.1 Lucene 的分段架构
很多候选人知道 ES 是基于 Lucene 的,但说不出 Lucene 的分段存储机制。
ES 的每个分片其实是一个 Lucene 索引,而每个 Lucene 索引又由多个 Segment(段)组成:
关键点:
- 每个 Segment 都是一个独立的倒排索引
- 新文档写入时先到 Write Buffer,定时 refresh 生成新 Segment
- 老 Segment 永不修改,只通过合并(Merge)被删除
3.2 段合并的代价
这就是生产环境中的经典坑:
在 ES 写入高峰期,如果触发了大量段合并,会出现明显的写入性能下降。合并过程是 CPU 和 IO 密集型操作,会占用大量资源。解决办法是:在业务低峰期执行 forcemerge,或者调整 merge 策略参数。
【面试官心理】 我问他 Segment,其实是想看他有没有踩过"段合并导致集群抖动"这个生产事故。能说出 merge policy 参数调优的,基本都带过生产环境。
四、集群节点角色 🟡
4.1 四大节点角色
ES 7.x 的节点角色分为以下几种:
4.2 最小高可用集群配置
很多候选人简历上写"负责 ES 集群运维",但被问到"Master 挂了怎么办"就答不上来。
生产最小高可用:
- 3 个 Master 候选节点(奇数,防止脑裂)
- 至少 2 个 Data 节点(保证副本能分配)
- 不推荐单节点集群用于生产(ES 会自动把副本分片分配到同一节点,失去了副本的意义)
:::details 📖 点击展开:脑裂问题详解 ES 6.x 及之前版本使用 ZenDiscovery 机制,如果网络抖动导致 Master 和部分节点失联,部分节点可能重新选举出一个新 Master,形成"两个 Master"的脑裂情况。解决方案:
- 奇数个 Master 候选节点
- 设置
discovery.zen.minimum_master_nodes = (master_eligible_nodes / 2) + 1 - ES 7.x 后引入
cluster.coordination.join_stability_threshold和 bootstrapping 流程,从根本上规避了脑裂 :::
五、生产避坑
5.1 容量规划翻车
线上后果:上线后发现单分片数据量超过 100GB,查询延迟从 50ms 飙升到 3s。
排查路径:
根本原因:创建 Index 时没有规划数据量增长,盲目使用默认分片数。
5.2 mapping 设计翻车
线上后果:某个字段类型设为 text,无法做范围查询和排序,只能用 script 字段解决,性能极差。
正确做法:需要排序/聚合的字段,类型设为 keyword;需要全文搜索的字段,设为 text + keyword 双类型:
六、工程选型
【面试官心理】 最后这道选型题,我是给他一个展示全局视野的机会。能说出"按时间分 Index + 别名"的,说明他对 ES 的数据生命周期管理有过实战经验。这种候选人,放在 P6+ 的水平线上。