Skip to content

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 查询慢怎么排查?

可以按顺序看:

  1. 查询是否命中大量分片。
  2. 是否用了深分页、大 size、大聚合。
  3. 过滤条件是否放在 filter。
  4. 排序字段是否正确使用 keyword/date/number。
  5. 是否对 text 字段做聚合或排序。
  6. 是否有脚本查询、通配符前缀、正则等高成本查询。
  7. 用 profile API 看每个查询子句耗时。
  8. 看 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 更有说服力。