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 分别适合哪些查询?
出现公司:暂无已核验的公开来源(本站自测)
考察重点:分词、精确匹配与列式字段读取。
参考回答
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. 参考资料
- Elasticsearch:Index and search analysis
- Elasticsearch:Near real-time search
- Elasticsearch:Index basics
- Elasticsearch 8.19:Near real-time search
- Elasticsearch 8.19:Translog
- Elasticsearch 8.19:Doc values
- Elasticsearch 8.19:Pagination
- Elasticsearch:Refresh parameter
- Elasticsearch 8.19:GET API
- Elasticsearch 8.19:Aliases
- Elasticsearch 8.19:Term query
- Elasticsearch 8.19:Replication model