Appearance
Elasticsearch 常见面试题
这篇整理一些 ES 常见面试题。面试时不要只背 API,更重要的是能把原理、使用场景和坑讲清楚。
ES 和 MySQL 有什么区别?
MySQL 是关系型数据库,擅长事务、约束、精确查询和结构化数据管理。ES 是分布式搜索与分析引擎,擅长全文检索、复杂过滤、聚合分析和相关性排序。
底层索引结构也不同:MySQL 常用 B+ 树,适合按字段值定位记录;ES 基于 Lucene 倒排索引,适合从词找到文档。
实际架构里,MySQL 通常作为权威数据源,ES 作为搜索视图。不要用 ES 替代需要强一致事务的业务库。
什么是倒排索引?
倒排索引是“词到文档”的映射。
普通正排是:
text
doc1 -> ES 是搜索引擎
doc2 -> MySQL 是数据库倒排是:
text
ES -> doc1
搜索引擎 -> doc1
MySQL -> doc2
数据库 -> doc2全文搜索时,根据查询词直接找到包含它的文档,不需要扫描所有文档。
ES 为什么是准实时?
写入文档后,数据先进入内存 buffer,并追加 translog。默认约 1 秒 refresh 一次,refresh 后新 segment 被打开,文档才可以被 search 查到。
所以 ES 不是写入后立刻搜索可见,而是 near real-time。
如果业务需要写入后立即可搜索,可以使用 refresh=wait_for,但会增加写入延迟,不适合所有写请求都开启。
refresh、flush、merge 有什么区别?
refresh:让新写入数据生成 segment 并对搜索可见。
flush:提交 Lucene commit point,持久化 segment,并清理旧 translog。
merge:把多个小 segment 合并成大 segment,同时清理删除标记。
一句话记忆:refresh 管可见,flush 管恢复,merge 管整理。
translog 是干什么的?
translog 是 ES 的事务日志,用于故障恢复。
写入时,ES 会把操作追加到 translog。节点异常重启后,可以用已经提交的 segment 加上 translog 重放,恢复最近写入。
translog 不用于搜索,搜索依赖的是 refresh 后打开的 segment。
ES 更新文档是原地更新吗?
不是。
Lucene segment 是不可变的,更新本质是:标记旧文档删除,然后写入新文档。删除也是先写删除标记,后台 merge 时才会物理清理。
所以 ES 不适合高频局部更新的强事务场景。
text 和 keyword 有什么区别?
text 会分词,适合全文检索,比如标题、正文。
keyword 不分词,适合精确匹配、排序、聚合,比如状态、标签、订单号。
常见设计是 text + keyword 多字段:
json
{
"title": {
"type": "text",
"fields": {
"keyword": { "type": "keyword" }
}
}
}term 和 match 有什么区别?
term 不会分析查询值,直接拿 value 去倒排索引匹配 term,适合 keyword、数字、日期等字段。
match 会先对查询内容分词,再拿分词结果去匹配,适合 text 字段。
如果拿完整中文句子对 text 字段做 term 查询,很容易查不到。
object 和 nested 有什么区别?
object 是默认对象类型。数组对象会被拍平成多值字段,可能丢失对象内部关系。
nested 会把数组里的每个对象作为隐藏子文档保存,可以保留对象关系,但查询和更新成本更高。
如果有如下数据:
json
{
"authors": [
{ "name": "小林", "age": 18 },
{ "name": "小王", "age": 30 }
]
}查询 name=小林 AND age=30,object 可能误命中,nested 可以避免。
什么是 Mapping 爆炸?
动态 JSON 字段不断产生新 key,ES 为每个 key 创建字段 Mapping,字段数量快速膨胀,这就是 Mapping 爆炸。
它会导致 cluster state 变大、内存压力增加、写入和查询变慢。
解决方式:
- 核心字段显式定义 Mapping。
- 动态扩展字段用 flattened。
- 只保存不搜索的对象设置
enabled: false。 - 限制用户自定义字段数量。
bool 查询里的 must、filter、should、must_not 区别?
must:必须匹配,会影响 _score。
filter:必须匹配,不影响 _score,适合硬过滤。
should:应该匹配,常用于 OR 或加分。
must_not:必须不匹配,不影响 _score。
注意:bool 里只有 should 时,默认至少匹配一个;如果已经有 must 或 filter,should 默认可能只是加分项,需要用 minimum_should_match 控制是否必须命中。
ES 深分页为什么慢?
from=10000&size=10 不是只取第 10001 到 10010 条。
每个分片都要取出足够多的候选结果,协调节点再全局排序、丢弃前面的结果。from 越深,分片和协调节点压力越大。
深分页建议使用 search_after,并提供稳定排序字段。
shard 数量怎么设置?
主分片数不是越多越好。
分片太少,单分片过大,迁移和恢复慢;分片太多,cluster state、文件句柄、内存和查询 fan-out 成本都上升。
经验上先根据数据量、增长速度、节点数、查询模式估算。日志类索引可以配合 rollover 和 ILM,让单分片保持在相对合理大小。
主分片数创建后不能直接修改,通常要新建索引并 reindex。
replica 有什么作用?
Replica 是主分片副本。
作用:
- 主分片所在节点故障时,副本可以提升为主分片。
- 搜索和读取可以走副本,分担读压力。
代价是写入要复制到副本,磁盘也要多占一份。常见配置是 1 个副本,根据可用性和读压力调整。
ES 查询慢怎么排查?
可以按顺序看:
- 查询是否命中大量分片。
- 是否用了深分页、大 size、大聚合。
- 过滤条件是否放在 filter。
- 排序字段是否正确使用 keyword/date/number。
- 是否对 text 字段做聚合或排序。
- 是否有脚本查询、通配符前缀、正则等高成本查询。
- 用 profile API 看每个查询子句耗时。
- 看 slow log、CPU、磁盘 IO、JVM GC。
ES 写入慢怎么排查?
常见原因:
- bulk 批次太大或太小。
- refresh 太频繁。
- replica 太多。
- translog/fsync 压力大。
- 磁盘 IO 打满。
- Mapping 过多或字段过复杂。
- ingest pipeline 或脚本处理太重。
- 热点 routing 导致某个分片过载。
批量导入时可以临时关闭 refresh、设置副本为 0,完成后恢复。
ES 数据如何保证和 MySQL 一致?
通常是最终一致,不是强一致。
常见方案:MySQL 写入成功后,通过 binlog、Canal、MQ 或 CDC 任务同步到 ES。ES 文档 _id 使用 MySQL 主键,保证幂等覆盖。
还需要补偿和校验机制,比如定时对账、失败重试、死信队列、按 updateTime 修复增量。
什么情况下不适合用 ES?
- 需要强事务一致性。
- 需要复杂多表 join。
- 需要高频小字段原地更新。
- 数据量很小,MySQL 索引已经能满足查询。
- 搜索条件简单,不需要全文检索或聚合分析。
ES 很强,但它不是所有查询问题的答案。
小结
ES 面试题看起来很多,其实主线很清楚:
- 倒排索引解释搜索为什么快。
- refresh/translog/segment 解释写入为什么准实时。
- shard/replica/协调节点解释分布式查询怎么做。
- Mapping/analyzer 解释为什么查得到或查不到。
- bool/filter/search_after 解释查询怎么写得稳。
能把这些概念用实际场景串起来,比单独背 API 更有说服力。