Elasticsearch面试要点:从倒排索引、分片到查询优化的系统梳理
前言
Elasticsearch 面试题通常不会只问“它是不是搜索引擎”。更常见的问题是:倒排索引是什么,写入为什么不是立刻可见,分片和副本怎么工作,查询如何路由,深分页为什么慢,聚合为什么占内存,Mapping 设计错了怎么办,集群变黄或变红怎么排查。
如果只把 Elasticsearch 理解成“可以全文搜索的数据库”,回答很容易停留在表面。更好的方式是把它理解成一条完整链路:
1 | 文档写入 Index -> 路由到 Primary Shard -> 写内存 Buffer 和 Translog -> Refresh 生成 Segment -> 查询时并发访问 Shard -> 合并排序和聚合结果 |
本文按面试中最常见的模块梳理 Elasticsearch 要点,适合作为面试前的复习清单。
Elasticsearch 整体定位
Elasticsearch 是基于 Lucene 的分布式搜索和分析引擎,擅长全文检索、结构化过滤、日志检索、指标分析、实时搜索和聚合分析。
它不是传统关系型数据库,也不适合替代所有主库场景。Elasticsearch 更适合作为搜索、检索、分析和可观测数据查询层,常与 MySQL、PostgreSQL、MongoDB、Kafka、Logstash、Beats 等系统配合使用。
面试中可以这样概括:
Elasticsearch 的核心能力来自 Lucene 倒排索引和分布式分片架构。倒排索引用于高效检索文本和字段,分片用于水平扩展,副本用于提升可用性和查询吞吐。
核心概念
Elasticsearch 常见概念如下:
- Index:索引,类似一类文档的逻辑集合。
- Document:文档,ES 存储和检索的基本单位,通常是 JSON。
- Field:字段,文档中的属性。
- Mapping:字段类型和索引方式定义。
- Shard:分片,一个 Index 会拆分成多个 Shard。
- Primary Shard:主分片,负责写入。
- Replica Shard:副本分片,用于容灾和分担读请求。
- Segment:Lucene 底层不可变索引段。
- Refresh:把内存中的新数据打开为可搜索 Segment 的过程。
- Translog:事务日志,用于异常恢复。
flowchart TD
A[Index] --> B[Primary Shard 0]
A --> C[Primary Shard 1]
B --> D[Replica Shard 0]
C --> E[Replica Shard 1]
B --> F[Segment]
C --> G[Segment]
倒排索引
倒排索引是 Elasticsearch 全文检索的基础。它不是从文档找词,而是从词找到包含这个词的文档。
例如有三篇文档:
1 | doc1: Java search engine |
倒排索引可以理解为:
1 | java -> doc1, doc3 |
查询 java search 时,ES 可以快速找到相关文档,再根据评分规则排序。
倒排索引通常包括:
- Term Dictionary:词典,记录所有词项。
- Posting List:倒排列表,记录词项出现在哪些文档中。
- Term Frequency:词频,影响相关性评分。
- Position:词项位置,用于短语查询。
- Offset:字符偏移,用于高亮。
面试中要强调:倒排索引适合检索,不适合频繁原地更新。Lucene Segment 是不可变的,更新本质上是标记旧文档删除并写入新文档。
分词和 Analyzer
全文检索依赖分词。Analyzer 通常包括 Character Filter、Tokenizer 和 Token Filter。
1 | 原始文本 -> 字符过滤 -> 分词 -> 小写/停用词/同义词等过滤 -> Token 列表 |
常见问题:
- 中文不能简单按空格分词,需要中文分词器。
- 查询分词和索引分词要匹配,否则可能查不到。
text字段会分词,适合全文检索。keyword字段不分词,适合精确匹配、排序和聚合。
例如:
1 | { |
这个设计同时支持 title 全文检索和 title.keyword 精确聚合或排序。
写入流程
Elasticsearch 写入流程的关键是 Primary Shard、Replica Shard、Buffer、Translog 和 Refresh。
sequenceDiagram
participant Client
participant Coord as Coordinating Node
participant Primary as Primary Shard
participant Replica as Replica Shard
participant Translog
participant Buffer as Index Buffer
Client->>Coord: Index Document
Coord->>Primary: 路由到主分片
Primary->>Translog: 写 Translog
Primary->>Buffer: 写内存 Buffer
Primary->>Replica: 同步到副本
Replica-->>Primary: 确认
Primary-->>Coord: 返回结果
Coord-->>Client: 写入成功
写入大致过程:
- 客户端请求任意节点,该节点成为协调节点。
- 协调节点根据文档 ID 或 routing 计算目标 Shard。
- 请求先到 Primary Shard。
- Primary 写入内存 Buffer 和 Translog。
- Primary 把写入复制到 Replica。
- 副本确认后返回客户端。
- 后续 Refresh 后文档才可被搜索到。
常见追问:为什么写入后马上搜索不到?
因为 ES 是近实时搜索,写入后需要 Refresh 生成新的可搜索 Segment。默认 Refresh 间隔通常是 1 秒,所以不是严格实时可见。
Refresh、Flush 和 Merge
Elasticsearch 中有几个容易混淆的概念:
- Refresh:让内存 Buffer 中的数据变成可搜索 Segment,代价相对较轻。
- Flush:提交 Lucene Commit,并清理 Translog,保证恢复成本可控。
- Merge:后台合并多个小 Segment,减少文件数量和删除标记。
1 | Index Buffer -> Refresh -> Segment 可搜索 |
Refresh 太频繁会影响写入吞吐;Refresh 太慢会影响数据可见性。Merge 跟不上会导致 Segment 过多,查询开销变大。
查询流程
Elasticsearch 查询是典型的分布式查询。一个查询通常分为 Query 阶段和 Fetch 阶段。
sequenceDiagram
participant Client
participant Coord as Coordinating Node
participant S1 as Shard 1
participant S2 as Shard 2
participant S3 as Shard 3
Client->>Coord: Search Request
Coord->>S1: Query
Coord->>S2: Query
Coord->>S3: Query
S1-->>Coord: Top N doc ids
S2-->>Coord: Top N doc ids
S3-->>Coord: Top N doc ids
Coord->>S1: Fetch docs
Coord->>S2: Fetch docs
Coord-->>Client: Merge and return
Query 阶段:
- 协调节点把查询请求发到相关 Shard。
- 每个 Shard 在本地执行查询和排序。
- 每个 Shard 返回 Top N 的文档 ID 和排序值。
- 协调节点合并全局 Top N。
Fetch 阶段:
- 协调节点根据最终命中的文档 ID 到对应 Shard 拉取
_source。 - 合并后返回客户端。
这也是深分页慢的原因之一:每个 Shard 都要返回较多候选结果,协调节点再做全局排序和丢弃。
Query 和 Filter
ES 查询语句中要区分 Query Context 和 Filter Context。
- Query:关注匹配程度,会计算相关性评分
_score。 - Filter:只判断是否匹配,不计算评分,通常可以缓存。
例如状态、类型、时间范围等条件更适合放到 filter 中:
1 | { |
面试中可以回答:需要相关性排序的条件放 Query,只做精确限制的条件放 Filter。
相关性评分
Elasticsearch 默认相关性评分基于 BM25。面试不一定要推公式,但要知道影响因素:
- 查询词是否命中。
- 查询词在文档中出现的频率。
- 查询词在整体语料中是否稀有。
- 字段长度,短字段中命中通常更有代表性。
- boost 权重设置。
常见优化方式:
- 给标题、标签、核心字段设置更高 boost。
- 用
multi_match控制多个字段权重。 - 用
function_score加入时间、热度、业务权重。 - 用
minimum_should_match控制匹配质量。
Mapping 设计
Mapping 是 Elasticsearch 使用效果的基础。字段类型选错,后续查询、排序、聚合和存储都会受影响。
常见原则:
- 需要全文搜索的字段用
text。 - 需要精确匹配、排序、聚合的字段用
keyword。 - 数值字段用对应 numeric 类型,不要用字符串。
- 时间字段用
date。 - 对象结构明确时使用
object,需要独立嵌套匹配时使用nested。 - 不需要检索的字段可以关闭
index。 - 控制动态字段,避免 Mapping 爆炸。
Mapping 通常不能随意修改已有字段类型。字段类型设计错了,常见处理方式是新建索引、定义正确 Mapping、Reindex 数据,再切换别名。
分片和副本
分片决定水平扩展能力,副本决定高可用和读吞吐。
Primary Shard 数量通常在索引创建时确定,后续调整成本较高。Replica 数量可以动态调整。
设计分片时要考虑:
- 数据总量。
- 单 Shard 大小。
- 查询并发。
- 节点数量。
- 写入吞吐。
- 后续增长空间。
分片不是越多越好。分片过多会增加集群元数据、文件句柄、内存和调度成本;分片过少会限制扩展能力。
常见经验是让单个 Shard 保持在合理大小范围内,并结合实际集群压测调整,而不是机械套固定数字。
集群状态
Elasticsearch 集群状态常见三种:
- Green:主分片和副本分片都正常分配。
- Yellow:主分片正常,部分副本未分配。
- Red:存在主分片未分配,部分数据不可用。
常见原因:
- 节点数量不足,副本无法分配。
- 磁盘水位过高,Shard 无法迁移或分配。
- 节点宕机。
- 分片过多,集群负载过高。
- 索引损坏或恢复失败。
排查时可以先看集群健康、未分配分片原因、节点磁盘、节点状态和恢复进度。
脑裂问题
早期 Elasticsearch 集群可能因为 Master 选举配置不合理出现脑裂:网络分区后多个节点都认为自己是 Master,导致元数据冲突。
现代版本通过集群协调机制降低了这类风险,但面试中仍可能问到。回答重点是:
- Master 负责集群元数据管理,不直接承担所有数据读写。
- Master 选举需要多数派原则。
- Master eligible 节点建议至少 3 个。
- 避免网络不稳定和错误的节点角色配置。
深分页问题
ES 的 from + size 深分页会越来越慢。
原因是每个 Shard 都要取出 from + size 条候选结果,协调节点再做全局排序,最后丢弃前面的 from 条。页数越深,浪费越大。
优化方式:
- 普通浅分页可以使用
from + size。 - 深分页或滚动导出使用
scroll。 - 用户连续翻页使用
search_after。 - 结合 Point In Time 保证分页过程中视图稳定。
- 避免允许用户任意跳到极深页。
聚合分析
Elasticsearch 聚合适合做日志统计、指标分析、分组计数、时间直方图等。
但聚合也容易消耗内存和 CPU,尤其是高基数字段聚合。
常见优化方式:
- 对聚合字段使用
keyword或数值类型。 - 避免对
text字段直接聚合。 - 控制 bucket 数量。
- 使用时间范围过滤减少数据量。
- 对高基数字段谨慎使用 terms aggregation。
- 必要时做预聚合或离线统计。
写入优化
批量写入 ES 时,重点关注吞吐、Refresh、Replica、Mapping 和 Bulk 大小。
常见优化:
- 使用 Bulk API,减少单条写入开销。
- 合理控制 Bulk 大小,避免请求过大导致内存压力。
- 大批量导入时临时调大 Refresh Interval。
- 初始导入时可以临时把 Replica 调为 0,导入完成后恢复。
- 使用固定 Mapping,避免动态字段过多。
- 避免频繁更新同一文档。
- 监控写入线程池、拒绝数、Refresh、Merge 和磁盘 IO。
查询优化
查询优化重点是减少扫描范围、减少评分成本、减少返回数据量。
常见方式:
- 精确条件放 filter。
- 时间范围、状态、租户等条件尽量前置过滤。
- 只返回必要字段,减少
_source体积。 - 避免通配符前缀查询,例如
*abc。 - 避免深分页。
- 对排序字段使用合适类型。
- 使用 Routing 缩小查询 Shard 范围。
- 对慢查询使用 Profile API 分析执行成本。
常见排查思路
如果面试官问“ES 查询突然变慢怎么排查”,可以按以下路径回答:
- 看是单个索引慢,还是整个集群慢。
- 看查询类型,是全文检索、过滤、排序、聚合还是深分页。
- 看是否命中大量 Shard,是否存在跨大量索引查询。
- 看慢查询日志和 Profile 结果。
- 看 Segment 数量、Merge 是否堆积。
- 看 JVM Heap、GC、CPU、磁盘 IO 和搜索线程池。
- 看是否有大批量写入、Refresh 频繁、聚合高基数或磁盘水位过高。
- 看 Mapping 是否合理,例如 text 字段被用于排序或聚合。
如果是写入变慢,可以重点看 Bulk 大小、写线程池拒绝、Translog、Refresh、Merge、Replica 同步和磁盘 IO。
高频面试题
Elasticsearch 为什么搜索快?
核心原因是倒排索引。ES 不需要逐条扫描文档,而是通过词项快速定位包含该词的文档,再结合评分、过滤和排序返回结果。
Elasticsearch 写入后为什么不能立刻查到?
ES 是近实时搜索。文档写入后先进入内存 Buffer 和 Translog,只有 Refresh 生成可搜索 Segment 后,搜索请求才能查到新文档。
text 和 keyword 有什么区别?
text 会分词,适合全文检索;keyword 不分词,适合精确匹配、排序、聚合和去重。很多字符串字段会同时设计 text 和 keyword 子字段。
为什么深分页慢?
因为每个 Shard 都要返回 from + size 条候选结果,协调节点合并排序后再丢弃前面的数据。页数越深,Shard 和协调节点浪费越大。
分片越多越好吗?
不是。分片太少会限制扩展,分片太多会增加元数据、内存、文件句柄和调度成本,也会让查询 fan-out 更重。分片数量应结合数据量、单分片大小、节点数和查询写入压力设计。
ES 可以当主库吗?
通常不建议把 ES 当强一致业务主库。ES 更适合作为搜索和分析系统。业务主数据通常放在关系型数据库或其他主存储中,再同步到 ES 做检索。
ES 集群 Yellow 一定有问题吗?
Yellow 表示主分片正常,副本分片未完全分配,数据仍可读写,但容灾能力下降。单节点集群设置了副本时常见 Yellow,因为副本不能和主分片放在同一节点。
总结
Elasticsearch 面试的主线可以围绕四个问题展开:
- 为什么能搜得快:倒排索引、分词、BM25。
- 数据怎么写进去:Primary Shard、Replica、Buffer、Translog、Refresh。
- 查询怎么跑起来:协调节点、Query 阶段、Fetch 阶段、排序和聚合。
- 线上怎么优化和排查:Mapping、分片、深分页、Merge、GC、磁盘和线程池。
把这条链路讲清楚,再结合 Mapping 设计、深分页治理、分片规划和集群状态排查,Elasticsearch 相关问题就能从概念回答升级成工程化回答。


