07 - Elasticsearch · 高频面试考题背诵指南


目录


一、核心原理

Q1:什么是倒排索引?和正排索引有什么区别?

核心考点:倒排索引的 Term → PostingList 映射结构,是 ES 全文检索的基石。

背诵话术

正排索引:以文档 ID 为 Key,记录文档中包含了哪些词(文档 → 关键词)。MySQL 的 B+Tree 索引就是正排索引。适合按 ID 精确查找文档,但不适合”搜索包含某关键词的所有文档”——需要全表扫描每篇文档的内容。

倒排索引:以词条(Term) 为 Key,记录这个词出现在哪些文档中(关键词 → 文档 ID 列表)。结构如下:

  • Term Dictionary(词条字典):所有去重后的词条,按字典序排序存储,使用 FST(Finite State Transducer,有限状态转换器) 做前缀压缩,内存占用极低。
  • Posting List(倒排列表):每个 Term 对应的文档 ID 列表,记录该词在哪些文档中出现、出现频率(TF)、出现位置(Position)等信息。多个 Posting List 之间的交集(AND)和并集(OR)就是布尔查询的本质。
  • Term Index(词条索引):Term Dictionary 的 B-Tree 索引,加速定位目标 Term,避免全量扫描字典。

搜索过程示例:搜索”招行”时,ES 先去 Term Index 找到”招行”在 Term Dictionary 中的位置,读取对应的 Posting List(文档 ID 列表),再根据这些 ID 去取文档内容,返回结果。整个过程不需要逐文档扫描。


Q2:ES 的写入流程(数据如何落盘)?

核心考点:BUFFER → OS Cache(refresh) → Translog → 磁盘(flush),近实时的物理原因。

背诵话术

ES 的写入流程涉及多个组件,核心是延迟写磁盘 + 写 Translog 保障可靠性

  1. 客户端发送一条写请求到某分片(路由到主分片,主分片写完转发到副本分片)。
  2. 数据先写入内存 Buffer(此时不可被搜索)。
  3. 同时将写入操作记录到 Translog(事务日志),Translog 每 5 秒 fsync 一次到磁盘(或每次写请求都 fsync,取决于 index.translog.durability 配置)。
  4. refresh(默认 1s 一次):将内存 Buffer 中的数据生成一个新的 Segment(段),写入 OS Cache(操作系统页缓存),此时数据变为可搜索。同时清空内存 Buffer。
  5. flush(默认 30 分钟一次 / Translog 达到 512MB):通过 fsync 将 OS Cache 中的 Segment 强制刷到磁盘,并清空旧的 Translog(生成新的 Translog 文件),形成段提交点(Commit Point)

关键点:refresh 之前的文档不可搜索;refresh 之后、flush 之前,文档在 OS Cache 中(内存可查但磁盘没有持久化),此时节点宕机会丢失数据,但可通过 Translog 回放恢复。


Q3:为什么 ES 是”近实时(Near Real-Time)”而非真正的实时?

核心考点:默认 1s 的 refresh 间隔是根本原因。

背诵话术

ES 被称为”近实时”(NRT),原因是:数据写入后不会立即被搜索到,必须等到默认每 1 秒执行一次 refresh 操作(生成新 Segment 并刷新到 OS Cache)之后,新数据才会对搜索可见。这 1 秒的 delay 就是”近”的来源。

如果要强制实时可见,可以在写入后显式调用 GET /index/_refresh API 主动触发 refresh,但这会消耗大量资源(频繁生成小 Segment),生产不推荐。

为什么 MySQL 也是近实时:类似地,MySQL InnoDB 的 Buffer Pool 中脏页不立即刷盘,事务提交后数据先写 Redo Log,通过 Checkpoint 机制定期刷盘,也有一段时间窗口。


Q4:ES 的查询流程(Query Then Fetch)?

核心考点:两阶段查询,协调节点先拿 ID 和评分,再按 ID 拉取文档内容。

背诵话术

ES 的查询分两个阶段Query 阶段 + Fetch 阶段

Query 阶段(打分 + 过滤)

  1. 协调节点(Coordinator Node)接收到查询请求,将请求转发给索引涉及的所有分片(每个主分片或副本分片)。
  2. 每个分片在本地执行查询(打分、排序、分页),将结果中的 文档 ID + 排序值_id_scoresort 字段)返回给协调节点,并返回分片级结果总数。
  3. 协调节点合并各分片返回的结果,按分数排序后,确定最终需要的文档 ID 列表(优先级队列)。

Fetch 阶段(内容拉取)

  1. 协调节点根据确定的文档 ID,向相关分片发送 Multi-Get 请求,拉取完整的文档内容(_source)。
  2. 取得完整的文档后,组装为最终响应返回给客户端。

为什么分两阶段:如果直接拉取所有分片的完整文档,网络传输数据量巨大,协调节点排序压力也很大。先拿轻量的 ID+分数,排序后再按需拉取内容,效率最高。


Q5:ES 深分页(from + size)为什么慢?如何解决?

核心考点:from+size 需要每个分片取出 top(from+size) 条,协调节点再合并排序丢弃前 from 条。

背诵话术

GET /index/_search { "from": 10000, "size": 10 } 慢的根本原因:

  • 假设有 5 个分片,请求 from=10000, size=10 时,协调节点要求每个分片都取出 Top 10010 条结果(文档 ID + 分数),总共需要处理 5 × 10010 = 50050 条数据
  • 协调节点在内存中合并这 50050 条数据并全局排序,再丢弃前 10000 条,只返回 10 条。
  • 分片越多、from 越大,内存和 CPU 消耗越高,ES 通过 max_result_window 默认限制 from + size ≤ 10000。

三种解决方案

方案 适用场景 原理 缺点
Scroll 全量数据导出、离线批处理 生成一个快照游标,后续翻页基于该快照顺序拉取 占用 ES 资源(有存活时间)、不支持实时数据、不可跳页
Search After(推荐) 实时搜索的深度翻页 每次查询记录最后一条的排序值,下次用 search_after 接续查询 只能点”下一页”,不能跳页、需配合唯一排序值(如 _id 做 tiebreaker)
限制翻页深度(业务层面) 用户搜索场景 对搜索结果限制最多翻到第 N 页(如百度只显示前 76 页),深度翻页需求走异步导出 不满足所有业务需求

二、集群架构

Q6:ES 集群的核心概念(Node / Shard / Replica)

核心考点:主分片负责写、副本分片负责读,分片数是水平扩展的基础。

背诵话术

ES 集群由以下核心概念组成:

  • Node(节点):ES 的服务实例。按角色分为:

    • Master Node:负责集群元数据管理(索引创建/删除、分片分配、节点上下线),不处理读写。通过 Bully 算法选举(ES 7.x 之后)。
    • Data Node:存储数据,处理查询和写入。是集群的工作主力。
    • Coordinating Node:接收客户端请求,分发到数据节点并汇总结果。每个节点都可以充当协调节点。
    • Ingest Node:对写入数据做预处理(如 Grok 解析日志、字段增加)。
  • Index(索引):逻辑上的数据库,划分为多个物理分片。

  • Primary Shard(主分片):数据的第一存放地,所有写请求先写入主分片。主分片数量索引创建时确定,不能修改(影响数据路由的 hash 算法)。

  • Replica Shard(副本分片):主分片的备份,提供高可用和分担查询压力。副本数可以动态调整。

数据路由shard = hash(_routing) % num_primary_shards(默认 _routing = _id),所以主分片数不能修改(改了 hash 结果全变)。

在招行项目中:规则库的检索量达百万级,我们部署 3 个 Data Node,设置每个索引 6 个主分片 + 1 个副本(共 12 个分片分布在 3 个节点上),确保单节点故障不影响搜索可用性。


Q7:ES 如何保证写一致性?

核心考点wait_for_active_shardsconsistency 配置,主分片 → 副本分片的同步写入。

背诵话术

ES 的写入一致性通过两个机制保证:

  1. wait_for_active_shards:控制写操作需要等待多少个分片确认才返回成功。wait_for_active_shards=1(默认)表示只要主分片写入成功就返回,不等待副本分片(性能高但有数据丢失风险);wait_for_active_shards=all 表示必须主分片 + 所有副本分片都写入成功。
  1. Translog 持久化:即使主分片返回成功后立即宕机,写入操作也会记录在 Translog 中,节点恢复时通过重放 Translog 恢复数据。类似 MySQL Redo Log 的设计。

与 Kafka ISR 的对比:ES 的 wait_for_active_shards=all 类似 Kafka 的 acks=all;ES 的 Translog 类似 Kafka 的 Partition Log。两者都是用”先写日志 + 异步刷盘”来平衡性能与可靠性。


三、ES vs MySQL 对比

Q8:ES 和 MySQL 在搜索场景下如何配合?

核心考点:各自适用的查询场景,强一致性数据在 MySQL,全文检索和聚合在 ES。

背诵话术

MySQL 擅长精确查询和事务操作,ES 擅长全文检索、模糊匹配、聚合分析。大规模系统里两者配合使用:

  • MySQL 作为 System of Record(记录系统):保存业务数据的权威版本。
  • ES 作为 Search Index(搜索索引):通过 Canal 或数据同步工具,将 MySQL 业务数据近实时同步到 ES,构建搜索索引。

招行项目的实际方案:调度规则配置数据存储在 MySQL,通过 Canal 订阅 Binlog,同时更新 Redis 缓存 + ES 搜索索引。业务人员按规则名称、业务线等关键词检索时,直接查 ES(毫秒级返回),不查 MySQL 做模糊 LIKE 查询(会导致全表扫描),配置详情查 Redis/MySQL 获取实时精确数据。

核心原则:ES 是衍生的搜索视图,MySQL 是权威数据源,数据不一致时以 MySQL 为准。


四、分片设计与优化

Q9:分片数和副本数的设计原则?

核心考点:分片数由数据量和写入 QPS 决定,副本数由查询 QPS 和可靠性要求决定。

背诵话术

  • 主分片数设计

    • 单个分片的数据量建议在 10~50GB,过大影响迁移和恢复速度,过小元数据开销大。
    • 分片数不宜过多:每个分片是一个 Lucene 索引,占用文件句柄和内存,ES 每个节点的分片数建议不超过堆内存的每 GB 20 个分片(如 8GB 堆 ≤ 160 个分片)。
    • 写并发高时,适当增加主分片数(写请求分散到更多主分片上并行处理)。
  • 副本数设计

    • 至少 1 个副本(保证单节点故障数据不丢失)。
    • 读 QPS 高时增加副本(副本可分担查询请求),但要付出额外的存储和写入同步成本。
    • 副本数 = N,意味着可以容 N 个节点故障同时数据不丢失。

招行项目配置:规则库数据量约 40GB,设置了 6 个主分片(每分片 6~7GB)+ 1 个副本,3 个 Data Node 各承载 4 个分片(包含主副混合),查询 QPS 高峰约 5000 时保持稳定。


五、面试必问对比题

Q10:ES 为什么比 MySQL 的 LIKE 查询快?

核心考点:倒排索引是 Term → DocID 的 O(1) 命中,LIKE ‘%xxx%’ 是全表扫描。

背诵话术

MySQL 的 LIKE '%xxx%' 前导通配符导致 B+Tree 索引完全失效(无法确定前缀,索引有序性用不上),必须全表扫描每行数据。百万行表做一次 LIKE 就扫百万行,并发一大直接打崩。

ES 通过倒排索引,把所有词条预先建立好 Term → DocID 的映射。搜索”招行”时,直接去 Term Dictionary 中二分查找该 Term,拿到 Posting List(包含该词的文档 ID 列表),整个过程是 O(logN + M)(N=词条数,M=匹配文档数),不需要逐文档扫描,比全表扫描快几个数量级。

核心差异:MySQL 是”读文档 → 找词条”(正排),适合精确查一条数据;ES 是”找词条 → 读文档”(倒排),适合”搜索包含某词的所有文档”。


Q11:ES 和 Solr 的选型对比?

核心考点:生态活跃度、实时性、分布式易用性。

背诵话术

  • ES 基于 JSON 的 RESTful API 更易上手,Solr 传统使用 XML。
  • ES 的分布式集群管理更成熟(自动分片分配、节点发现),Solr 依赖 ZooKeeper。
  • ES 社区活跃度远超 Solr,与 ELK(Elasticsearch + Logstash + Kibana)生态深度绑定。
  • 所以生产项目(包括招行)选型时,ELK 生态的日志检索、监控告警是一大加分项。