Elasticsearch 原理大纲
这份你要建立的不是“记几个 DSL”,而是一整条 Elasticsearch 原理链:
文本 -> 分词 -> 倒排索引 -> segment -> refresh / merge -> shard / replica -> query / fetch -> 为什么适合搜索但不适合做主库
一、总纲
Elasticsearch 原理层最核心的 6 条主线:
- 倒排索引与全文检索
- analyzer / mapping / text / keyword
- segment 与近实时搜索
- shard / replica 与分布式检索
- query / fetch 流程
- 为什么它适合搜索、不适合主事务库
二、为什么 Elasticsearch 能做全文检索
数据库最常见的 B+ 树索引,更适合:
- 等值查询
- 范围查询
- 排序
但全文检索更关注的是:
- 一段文本里有哪些词
- 哪些文档包含某个词
- 多个词如何组合查询
这时倒排索引就更合适。
你要抓住的核心
不是“文档里有什么词”,而是“词出现在哪些文档里”。
这就是为什么 ES 对关键词搜索、全文检索、复杂筛选会比数据库 like '%xx%' 自然得多。
三、倒排索引原理链
1. 什么是倒排索引
倒排索引会把文本拆成 term,然后按 term 建索引。
例如:
- 文档 1:
apple phone - 文档 2:
apple watch - 文档 3:
android phone
倒排结构更像:
apple -> [1, 2]phone -> [1, 3]watch -> [2]android -> [3]
2. 为什么它快
因为查 apple 时,不需要全表扫所有文档,而是直接命中:
apple这个 term- 它对应的 posting list
3. 为什么它适合搜索
因为搜索本质上经常是:
- 查一个词
- 查多个词组合
- 查词频、相关性
- 查包含关系
倒排索引天然就是为这类场景设计的。
四、分词与 analyzer 原理
搜索效果很多时候不只是 DSL,而是分词是否合理。
analyzer 通常包含几步
- character filter
- 先做字符级处理
- tokenizer
- 把文本切成 token
- token filter
- 再做小写化、停用词、同义词等处理
为什么这块重要
因为一旦 analyzer 设计不对:
- 搜索不到
- 搜索太宽
- 排序不合理
- 中英文效果差
很多“ES 不准”的问题,根因不是查询语句,而是 mapping 和 analyzer。
五、mapping、text、keyword 原理
1. 为什么 mapping 很重要
因为 ES 不是简单“来什么字段就存什么”,字段类型会直接影响:
- 是否分词
- 是否能聚合
- 是否适合排序
- 索引结构怎么建
2. text
- 会分词
- 适合全文检索
- 不适合直接拿来精确聚合和排序
3. keyword
- 不分词
- 适合精确匹配
- 适合排序、聚合、过滤
4. 工程上的常见做法
一个字段经常会同时有:
text版本负责搜keyword子字段负责过滤 / 排序 / 聚合
这和数据库设计一样,本质上也是“按查询模式设计索引”。
六、segment、refresh、flush、merge
这块是 ES 很容易被问到的近实时原理。
1. 写入后为什么不是立刻可搜索
因为 ES 写入后,不是每一条都立刻变成“完全可检索”的最终状态。 它通常会先进入内存缓冲和 translog,再经过 refresh 才对搜索可见。
2. refresh 是什么
refresh 的核心是:
- 把新写入的数据生成新的可搜索 segment
- 让这些数据对搜索可见
所以 refresh 更偏“搜索可见性”,不是“彻底完成所有底层整理”。
3. flush 是什么
flush 更偏:
- 把内存和 translog 状态做更稳定的落盘整理
4. merge 是什么
segment 会不断增多,如果一直不整理:
- 查询要扫更多 segment
- 管理成本变高
- 碎片变多
所以后台会不断 merge,把多个小 segment 合并成更大的 segment。
5. 为什么说 ES 是近实时
因为:
- 写入成功
- 不等于立刻搜索可见
通常要等 refresh 之后才稳定被检索到。 这就是它和事务数据库“提交即强可见”的重要差异。
七、shard 与 replica 原理
1. shard 在解决什么
一个索引太大时,不可能总靠单机单分片承载。 所以 ES 会把索引拆成多个 shard。
shard 主要解决:
- 容量问题
- 并行问题
- 分布式扩展问题
2. replica 在解决什么
副本主要解决:
- 高可用
- 读扩展
- 主分片故障恢复
3. 为什么分片不是越多越好
因为分片越多,协调成本也会越高:
- 更多元数据
- 更多内存占用
- 更多 query / fetch 汇总成本
所以分片设计本质上是:
- 容量
- 并发
- 运维成本
三者的平衡。
八、查询流程原理
一次搜索请求,常见会经过两个阶段:
1. query phase
协调节点把请求发到相关分片,各分片先本地算:
- 哪些文档匹配
- 分数是多少
- 本分片 top N 是谁
2. fetch phase
协调节点汇总各分片结果,再去取真正需要返回的文档详情。
3. 为什么要这样设计
因为如果一上来就让所有分片把大量完整文档都传回来,网络和内存成本都会很高。 所以 ES 更像是:
- 先局部筛选
- 再全局归并
- 最后按需取详情
九、写入流程原理
简化理解通常是:
- 请求写到主分片
- 主分片写内存结构和 translog
- 再同步到副本分片
- refresh 后新文档对搜索可见
这条链路很重要,因为它解释了:
- 为什么 ES 更偏近实时
- 为什么它更像“查询镜像”
- 为什么不能把它当成主事务库
十、为什么 ES 适合搜索,但不适合主事务库
它适合什么
- 全文检索
- 搜索排序
- 多条件筛选
- 检索型聚合
它不适合什么
- 强事务主状态
- 支付 / 订单 / 退款主事实
- 需要严格提交即强一致可见的核心业务
最重要的工程理解
在真实项目里,更稳的做法通常是:
- MySQL 做主状态
- ES 做查询镜像
- 通过 MQ / binlog / 异步任务去同步
接受短暂延迟,但不让 ES 决定主事实。
十一、搜索慢时原理上优先怀疑什么
1. mapping / analyzer 设计错了
- 本该 keyword 的做成了 text
- 本该 text + keyword 双字段的只做了一种
- 分词器选错
2. 查询 DSL 太重
- 全文检索叠很多过滤和聚合
- 排序过重
- 脚本查询过多
3. 分片设计不合理
- 分片过多
- 热点索引
- segment 太碎
4. 深分页
from + size太大- 排序归并成本过高
十二、为什么深分页慢
因为深分页不是“只拿第 10001 到 10020 条”这么简单。 它往往意味着:
- 每个分片都要先找到前面大量候选结果
- 协调节点再做全局归并
- 前面大量结果最后被丢弃
所以深分页常用:
search_after- scroll
- 限制最大页数
1. 为什么 from + size 会随着页码变深而越来越贵
因为 ES 的搜索通常是分片并行的。
当你查很靠后的页时,常见过程是:
- 每个 shard 先找自己的前
from + size个候选 - 协调节点把这些候选再做一次全局排序
- 前面的
from大量结果最后被丢弃
所以深分页真正贵的地方在于:
- shard 本地 topN 变大
- 协调节点 merge sort 压力变大
- 内存和 CPU 一起上涨
这也是为什么 ES 默认会用:
index.max_result_window = 10000
去限制 from + size 深度。
2. search_after 在原理上为什么更合适
search_after 的核心思路不是“跳过前面很多条”,而是:
- 用上一页最后一条的 sort 值作为游标
- 从这个游标之后继续取下一页
这意味着它更像:
- 顺着排好序的结果继续往后拿
而不是:
- 每次从头开始再丢一大堆
所以它特别适合:
- 连续向后翻页
- 无限滚动
- “加载更多”
3. 为什么 search_after 最好配合 PIT
如果翻页过程中索引刷新了,结果顺序可能变化。
这时候即便你用了 search_after,也可能出现:
- 某些文档重复
- 某些文档漏掉
PIT,point in time,本质上就是:
- 在这次分页期间固定一个索引视图
所以可以这样理解:
search_after解决“不要反复丢前面的结果”PIT解决“翻页过程中结果集漂移”
4. scroll 应该怎么理解
scroll 更偏:
- 批量遍历
- 数据导出
- 离线处理
而不是给用户做实时搜索翻页。
因为它本质上会维护一段搜索上下文,更像扫描数据,而不是前台交互分页。
5. 真正完整的优化思路
深分页优化不能只停在“换个 API”,更完整的是:
- 普通浅分页继续
from + size - 连续翻页改
search_after - 需要稳定视图时配
PIT - 批量导出用 scroll
- 限制最大深度
- 从产品交互上减少“无限翻页”的需求
6. 一句最稳的总结
ES 深分页慢,本质上是分片查询下的大量候选结果归并和丢弃成本过高;真正的优化不是简单调大窗口,而是根据场景在 from+size、search_after+PIT、scroll 和交互限制之间做正确选择。
十三、ES 在你的项目里应该怎么讲
你更适合的表达不是“我会搭 ES 集群”,而是:
我知道 ES 在系统里应该放哪一层。像搜索、后台筛选、导入后检索这类场景,ES 很适合;但订单、支付、退款、订阅这些主状态还是 MySQL。我的重点是让 ES 成为高效查询层,而不是让它和主事务库职责混乱。
十四、你至少要讲顺的一条原理链
文档写入 -> analyzer 分词 -> 倒排索引 -> segment 生成 -> refresh 后搜索可见 -> 多 shard 查询 -> query/fetch 汇总 -> 返回结果
只要这条链能讲顺,ES 原理层就不会显得空。
十五、最适合你的结尾表达
我对 Elasticsearch 的理解不是停留在“会不会写 DSL”,而是理解它为什么适合全文检索、倒排索引为什么高效、segment 和 refresh 为什么让它成为近实时搜索系统,也知道它和 MySQL 的职责边界应该怎么划。