跳到主要内容

Elasticsearch

Elasticsearch 用倒排索引支持全文检索与过滤。本文以自管 Elasticsearch 8.19 为准。范围包括检索结构、搜索可见性和索引同步。

1. 面试核心​

先记住这几条,再读正文
  • 索引结构。 倒排索引按 term 查文档,doc values 按文档读取字段值。卡 · 倒排索引
  • 近实时搜索。 refresh 让新变化进入搜索视图,实时 GET 可读取尚未 refresh 的文档。卡 · 综合题
  • 故障恢复。 flush 执行 Lucene commit,translog 用于恢复尚未 commit 的操作。卡 · refresh、flush 与 merge
  • 刷新策略。 refresh=wait_for 等待写入可搜索,refresh=true 主动刷新相关分片。
  • 深分页。 深分页可用 PIT 固定搜索视图,再用 search_after 继续读取。卡 · 深分页

2. 倒排索引​

倒排索引保存 term 到文档的映射。posting list 记录含有该 term 的文档及检索信息。全文查询先定位词项,再组合文档集合和评分。

按文档 ID 读取 _source、排序与聚合有各自的数据结构。doc values 按文档组织字段值,适合排序和聚合。倒排索引则适合按词项查找文档。

3. analyzer​

analyzer 包含一个 tokenizer。它还可包含字符过滤器和 token filter。分析结果决定索引中保存哪些 term。

字段适用操作
text全文分析,配合 match 等查询。
keyword精确值过滤、聚合和排序。

match 会分析输入文本。term 直接匹配索引中的词项。对 text 直接使用 term 时,输入必须匹配分析后的词项。

keyword 默认支持 doc values。text 默认没有 doc values,聚合需要合适的 keyword 子字段。开启 text 的 fielddata 会增加堆内存成本。

例如标题可同时有 title 和 title.keyword。全文检索查询 title,精确统计使用 title.keyword。索引与搜索 analyzer 应产生可匹配的词项。

4. 写入结果与搜索可见性​

写入改变 Lucene 内部结构,并记录 translog。refresh 打开新的搜索视图,近期变化才可被 search 读取。按 ID 的 GET 默认是实时读取,读取路径不同。

自管 8.19 默认周期为 1 秒。默认周期刷新针对最近 30 秒内被搜索过的索引。负载和配置会影响实际等待,因此该间隔并非延迟上限。Elastic Cloud Serverless 的默认 refresh 间隔为 5 秒。

refresh=wait_for 等待本次变化可见,再返回响应。refresh=true 主动刷新相关分片。频繁主动刷新会增加小 segment 和合并成本。

5. refresh、flush 与 merge​

操作作用
refresh更新可搜索视图。
flush执行 Lucene commit,并开启新一代 translog。
merge合并 segment,并回收已删除文档的空间。

8.19 默认 index.translog.durability=request。成功响应前,主分片和已分配副本完成 translog 同步。尚未包含在 Lucene commit 的操作,可通过 translog 恢复。

设置为 async 后,translog 按同步周期刷盘。默认 index.translog.sync_interval=5s。崩溃会丢失上次同步之后的操作,包括已响应成功的写入。

因此,refresh 与持久性分属两个判断。flush 也不是每次写入安全的唯一来源。已无恢复用途的旧 translog 可在后续清理。

6. 分片​

文档根据 routing 映射到主分片。未指定 routing 时,默认使用文档 ID。主分片处理写入,再复制到副本。

搜索在每个相关分片选取一个可用副本执行。协调节点合并候选结果,再读取所需文档。请求扇出越多,协调和资源成本也越高。

自定义 routing 可以减少查询涉及的分片。写入与读取要使用相同路由规则。热点路由会把流量集中到单个分片。

7. 深分页​

from + size 分页需要各分片维护前面所有候选结果。页码越深,内存和排序开销越高。8.19 的 index.max_result_window 默认是 10000。

search_after 使用上一页最后一条的排序值继续查询。查询条件和排序必须保持一致。排序还需有稳定的并列处理字段。

PIT 固定搜索视图,避免翻页间 refresh 改变结果。PIT 搜索隐含 _shard_doc 作为并列排序字段。客户端应续期并使用返回的最新 PIT ID,完成后关闭。该方案适合顺序翻页,随机跳页需另外设计。

8. 与数据库同步​

数据库与搜索索引分别提交,直接双写存在失败窗口。可用本地 Outbox 或 CDC 生成可恢复变更。事件带稳定文档 ID 和业务版本。

索引写入应校验版本,防止旧事件覆盖新状态。确定文档 ID 只解决重复插入,乱序仍需版本控制。业务版本处理见消息幂等性。

9. 对账与重建​

定期比较权威数据与索引的版本、数量和关键字段。重建时创建新索引,导入快照后追赶增量。完成核对再通过一次 alias 更新切换读取目标。

mapping 中的字段类型变更可通过新索引迁移。旧索引保留期间若未继续同步,回切后会读到旧状态。回退前也要核对增量进度。

10. 常见问题​

10.1 写入成功与搜索缺失​

先比较实时 GET 和 search。GET 可见而 search 缺失时,检查 refresh 和查询条件。两者都缺失时,再查目标索引、routing 和写入结果。

10.2 搜索索引与权威状态​

11 · 面试题卡片​

11.1 Elasticsearch 为什么写入后不能立即搜索,如何与数据库保持一致​

出现公司:去哪儿、字节跳动

题型:综合题

考察重点

  • analyzer、term、posting list 与字段 mapping。
  • refresh、实时 GET 和近实时搜索。
  • Outbox、CDC、幂等版本和索引重建。

相关内容:倒排索引至对账与重建。

参考回答

写入成功与搜索视图更新发生在不同阶段。

近期变化经过 refresh 后,普通 search 才能读取。按 ID 的实时 GET 可读取尚未 refresh 的文档。

数据库同步可通过 Outbox 或 CDC 产生恢复事件。消费者按稳定 ID 和业务版本更新索引。定期对账发现遗漏,重建后追赶增量并切换 alias。

11.2 text、keyword 和 doc values 分别适合哪些查询?​

出现公司:暂无已核验的公开来源(本站自测)

考察重点:分词、精确匹配与列式字段读取。

相关内容:倒排索引、analyzer。

参考回答

text 适合分析后的全文查询,keyword 适合精确值匹配。

doc values 按文档读取字段,支持排序和聚合。text 默认没有 doc values,统计可使用 keyword 子字段。term 查询也要求输入匹配实际索引词项。

11.3 refresh 后就安全落盘了吗,flush 前的成功写入靠什么恢复?​

出现公司:暂无已核验的公开来源(本站自测)

考察重点:搜索可见性与恢复日志。

相关内容:refresh、flush 与 merge。

参考回答

refresh 让写入可搜索,故障恢复依靠 translog 持久化和 Lucene commit。

Elasticsearch 8.19 默认 index.translog.durability=request,成功响应前同步 translog;尚未进入 Lucene commit 的操作可由它恢复。flush 执行 Lucene commit 并开启新日志代。改为 async 后,上次同步之后的写入存在丢失窗口。

11.4 深分页为什么用 search_after 配合 PIT?​

出现公司:暂无已核验的公开来源(本站自测)

考察重点:候选集合成本与稳定搜索视图。

相关内容:深分页。

参考回答

深 from 分页要保留前面所有候选结果。

search_after 从上一页排序值继续,减少这部分开销。PIT 固定搜索视图,防止翻页期间数据变化。排序仍需稳定,客户端还要续期和关闭 PIT。

12. 参考资料​