第 13 章 Elasticsearch:搜索与索引
Elasticsearch 的价值不在于“能查文本”,而在于它把全文检索、过滤、聚合和分布式扩展组合成了一套可落地的搜索系统。面试和工程实践里,真正拉开差距的不是会不会写 Query DSL,而是能否解释清楚倒排索引、分片副本、写入刷新、相关性排序和线上治理之间的关系。
倒排索引与搜索基础
关系型数据库更擅长精确查找和事务处理,而搜索系统更擅长从大量文本中找出“最相关”的结果。支撑这一点的核心结构就是倒排索引。
倒排索引会把“词项到文档”的关系提前建好。以酒店搜索为例,文档里出现过“Jakarta”“airport”“hotel”这些词,索引中就会记录这些词分别出现在哪些文档、出现次数多少、位置在哪。查询时不再遍历全部文档,而是直接定位词项对应的 posting list,再做交并集、过滤和打分。
| 结构 | 作用 |
|---|---|
| Document | 一条被索引的业务记录 |
| Term | 分词后得到的词项 |
| Posting List | 某个词项命中的文档列表 |
| Segment | Lucene 的底层不可变索引段 |
| Analyzer | 文本分析链路,负责分词与归一化 |
这种模型决定了 Elasticsearch 适合搜索、筛选、统计三类操作的组合,但不适合高频事务更新、强一致约束和复杂联表。工程上一个常见误区是把它当主数据库使用,结果在更新频繁、字段约束严格的场景里付出很高代价。
索引、分片与副本
索引可以理解成一类文档的逻辑集合,而真正承载数据的是分片。主分片负责承接写入与查询,副本分片用于容灾和读扩展。
| 概念 | 作用 | 设计关注点 |
|---|---|---|
| Index | 逻辑数据集 | 一般按业务域或生命周期拆分 |
| Primary Shard | 主分片 | 决定水平扩展上限,创建后不易直接调整 |
| Replica Shard | 副本分片 | 提升可用性和查询并发 |
| Routing | 路由规则 | 影响写入均衡、查询范围和热点风险 |
分片不是越多越好。分片过多会带来更多元数据、更多小段文件和更重的协调开销;分片过少则会限制并行度和扩容空间。经验上更重要的是让单分片大小、文档量、查询模式和节点资源处于平衡状态。
副本也不是单纯“多一份更安全”。副本数增加后,读吞吐和容灾能力会上升,但写入链路需要等待更多复制动作,磁盘和网络成本也会增加。面试里经常会追问“分片和副本分别解决什么问题”,要回答清楚:分片解决容量和并行,副本解决可用性和读扩展。
写入链路与查询链路
Elasticsearch 的写入不是“直接落成可查的数据结构”,而是先经过协调、路由、写入内存缓冲与 translog,再在 refresh 后对搜索可见。
一条文档写入链路通常可以拆成以下阶段:
- 客户端请求先到协调节点。
- 协调节点根据
_id或自定义 routing 计算目标主分片。 - 主分片先写内存 buffer 和 translog,再并行复制给副本。
- 达到确认条件后向客户端返回成功。
- 后台 refresh 把内存中的数据转换成新的 segment,使其对搜索可见。
这也是为什么 Elasticsearch 常被称为 near real-time search。写成功不代表立刻能被检索到,中间存在一个 refresh 窗口。
查询链路同样分为两段:
- Query Phase:协调节点把查询分发到相关分片,各分片本地完成匹配、过滤和打分。
- Fetch Phase:协调节点汇总 Top N 命中文档,再向对应分片拉取
_source等完整内容。
如果没有 routing 限定,查询通常会广播到多个分片。分片数越多,跨分片协调成本越高,因此“查询慢”不一定是单机算力不足,也可能是索引设计把简单请求放大成了全局扫描。
Mapping、分词与相关性
Mapping 决定字段如何被索引和查询,是搜索质量与资源成本的分水岭。最常见的字段选择是 text 和 keyword:
| 字段类型 | 适用场景 | 特点 |
|---|---|---|
text | 标题、描述、评论等全文字段 | 会分词,适合 match 查询 |
keyword | ID、状态、国家码、精确标签 | 不分词,适合过滤、聚合、排序 |
一个常见实践是为同一业务字段同时保留全文与精确子字段,例如名称字段既支持中文分词搜索,也支持 .keyword 做精确匹配和聚合。
Analyzer 一般由字符过滤、Tokenizer、Token Filter 三部分组成。中文场景常见的分词策略是索引时更细、查询时稍粗,用来兼顾召回和精确度。若分词器选择不当,常见问题包括:
- 召回过低:用户输入的词被切得太碎或根本切不出来。
- 噪声过高:停用词、同义词配置不合理,结果相关性下降。
- 聚合错误:把本该用
keyword的字段误建成text。
相关性评分常以 BM25 为基础。它综合考虑词频、逆文档频率和字段长度,因此高频词、长文本和多字段查询都会影响最终排序。线上搜索体验差,往往不是“ES 算法不行”,而是字段权重、分词规则、过滤条件和业务排序混在一起没有分层治理。
深分页与性能优化
深分页是 Elasticsearch 的经典问题。from + size 在页码很深时,协调节点仍需要让各分片先取回更多候选,再做全局排序和截断,因此页数越深,CPU、内存和网络成本越高。
| 方案 | 适用场景 | 特点 |
|---|---|---|
from + size | 浅分页、后台列表 | 简单直接,但深页成本高 |
| Scroll | 批量导出、离线扫描 | 基于快照,不适合实时翻页 |
search_after | 面向用户的连续翻页 | 依赖稳定排序,性能更可控 |
除了分页,性能优化还要同时看写入、查询和索引模型三层。
写入侧重点:
- 批量写入而不是单条刷入。
- 控制 refresh 和副本策略,避免高峰期频繁生成小 segment。
- 让路由更均匀,避免热点主分片。
查询侧重点:
- 能 filter 就不要全靠 score。
- 缩小搜索范围,例如按时间、租户、业务域切索引。
- 对高频聚合与排序字段提前建好合适的数据类型。
索引侧重点:
- 避免过宽 mapping 和无意义的动态字段爆炸。
- 控制单分片大小,减少过多小分片。
- 对冷热数据分层,降低热节点负担。
集群治理与常见问题
搜索集群的事故很多不是出在“查不出来”,而是出在治理:脑裂风险、分片失衡、写入堆积、段合并抖动、磁盘水位过高、mapping 失控。
常见问题与排查重点可以归纳如下:
| 问题 | 常见原因 | 处理方向 |
|---|---|---|
| 写入延迟升高 | bulk 太小、refresh 太频繁、磁盘吃紧 | 调整批量、观察 translog 和 merge |
| 查询抖动 | 深分页、跨太多分片、缓存命中差 | 收缩查询范围,优化排序和过滤 |
| 分片不均衡 | routing 偏斜、热点租户集中 | 重设路由策略或拆索引 |
| 磁盘高水位 | 副本过多、冷热分层缺失、保留周期过长 | 清理旧索引,做 ILM 或冷热迁移 |
| 集群不稳定 | 主节点选举异常、节点抖动、GC 压力大 | 先稳控制面,再排查 JVM 和硬件 |
旧版本 Elasticsearch 里常被问到“脑裂”问题,本质是网络分区下多个节点都认为自己可以成为主节点。现代集群通过更严格的选主与法定人数机制降低了这类问题,但工程原则没有变:控制面节点要稳定,选主规则要清楚,不能把主节点和重查询节点混成一团。
日常治理最值得监控的维度包括:
- 集群健康:节点数、主分片/副本分片分配、未分配分片。
- 写入链路:bulk 延迟、拒绝数、translog、refresh 和 merge 时间。
- 查询链路:QPS、P95/P99 延迟、慢查询、缓存命中率。
- 资源层:CPU、堆内存、GC、磁盘水位、网络带宽。
本章小结
Elasticsearch 的核心不是某个 API,而是“倒排索引 + 分片副本 + 近实时刷新 + 相关性排序”这套整体模型。
可以把本章总结为四个判断:
- 做搜索系统时,先想倒排索引和分词,而不是 SQL 心智。
- 做集群规划时,先分清分片解决扩展,副本解决高可用。
- 做性能优化时,先找出是写入、查询还是索引模型出了问题。
- 做线上治理时,先稳住控制面、分片布局和磁盘水位,再谈复杂调优。
本章面试题与追问
-
Elasticsearch 和 MySQL 在检索模型上有什么根本区别? 追问:为什么全文检索不适合用普通 B+ 树硬扛?
-
什么是倒排索引?它的核心结构包括哪些部分? 追问:posting list 里为什么不仅要记文档 ID,还常常要记位置和词频?
-
分片和副本分别解决什么问题? 追问:为什么分片数不是越多越好?
-
Elasticsearch 的写入为什么是 near real-time,而不是绝对实时? 追问:translog、refresh、segment 各自起什么作用?
-
一次搜索请求大致会经过哪些阶段? 追问:为什么跨很多分片的查询更容易变慢?
-
text和keyword有什么区别? 追问:如果把聚合字段错误地建成text,线上会有什么后果? -
Analyzer 的作用是什么? 追问:为什么搜索质量问题很多时候本质上是分词和字段设计问题?
-
BM25 主要在衡量什么? 追问:为什么业务排序经常需要和相关性评分拆开设计?
-
深分页为什么慢?
scroll和search_after分别适合什么场景? 追问:如果用户要持续翻页看搜索结果,你会优先推荐哪种方案? -
Elasticsearch 集群线上最常见的治理问题有哪些? 追问:如果磁盘水位持续升高且分片未分配,你会先检查什么?