Elasticsearch
Elasticsearch 的后端工程主笔记:从倒排索引、Mapping、Analyzer、Segment、Near Real-time Search,到 Shard/Replica、聚合、写入链路、容量与选型边界。
[!info] related notes
Elasticsearch
[!abstract] 一句话定位 Elasticsearch 是建立在 Lucene 之上的分布式搜索与分析引擎,擅长全文检索、过滤、相关性排序和聚合。它通常是由业务事实同步出的搜索投影,不应仅因为能存 JSON 就默认成为业务唯一数据库。
核心数据模型
| 对象 | 含义 |
|---|---|
| Document | 一个 JSON 文档 |
| Index | 一组相关文档的逻辑命名空间 |
| Mapping | 字段类型以及如何索引/查询 |
| Primary Shard | 文档水平分片,每个文档属于一个 Primary |
| Replica Shard | Primary 的副本,提供冗余和读能力 |
| Segment | Lucene 内部不可变索引段 |
[!warning]
Index的双重含义 Elasticsearch Index 是文档集合;关系型数据库 Index 是表上的访问结构。两者不能直接类比。
倒排索引
普通存储从文档看词:
Document → Terms
倒排索引从词找到文档:
term "kafka" → doc 12, doc 81, doc 104
因此全文检索不必逐文档扫描正文。实际搜索还会结合词频、字段、长度等信息计算相关性。
Mapping:Text 与 Keyword
text
用于全文检索,通常经过 Analyzer:
Character Filter → Tokenizer → Token Filter → Terms
适合文章标题、正文、描述等自然语言。
keyword
把整个值作为精确 Term,适合:
- ID;
- Enum / Status;
- Tag;
- Exact Match;
- Aggregation / Sort。
错误 Mapping 会直接影响检索正确性和资源成本。生产中应优先显式 Mapping / Template,而不是无限依赖动态映射。
Inverted Index 与 Doc Values
Inverted Index
term → documents
→ search / filter
Doc Values
field-oriented values
→ sort / aggregation / scripting
全文检索和聚合使用的主要数据结构不同,因此字段类型也会影响磁盘、Heap 和查询方式。
Segment 与 Near Real-time Search
Lucene Shard 由多个不可变 Segment 构成。新写入先进入内存索引缓冲区,Refresh 后形成可被搜索打开的新 Segment:
Index Request acknowledged
→ in-memory indexing buffer
→ Refresh
→ new Segment becomes searchable
默认情况下,新文档通常在秒级变得可搜索,因此称为 Near Real-time,而不是立即可见。
Write acknowledged ≠ Search immediately sees it
频繁强制 Refresh 会增加 Segment 和 Merge 压力。
Translog、Refresh 与 Flush
- Refresh:让最近写入对 Search 可见。
- Translog:为尚未进入安全 Lucene Commit 的操作提供恢复路径。
- Flush:推进 Lucene Commit 并开始新的 Translog Generation。
它们分别解决搜索可见性与崩溃恢复,不应都笼统理解为“刷盘”。
Shard 与 Replica
Index
├─ Primary Shard 0 → Replica 0
├─ Primary Shard 1 → Replica 1
└─ Primary Shard 2 → Replica 2
- Primary Shard 数量决定基础数据分布和并行度,标准 Index 创建后不能直接随意修改。
- Replica 数量可调整,用于冗余和读扩展。
- 每个 Shard 本质上是独立 Lucene Index,也有 Segment、文件句柄、Heap 元数据和恢复成本。
因此 Shard 不是越多越好。
写入与查询链路
写入
Client
→ Coordinating Node
→ route by document id
→ Primary Shard
→ Replica Shards
→ response
查询
Client
→ Coordinating Node
→ scatter query to relevant shards
→ each shard returns local top results
→ coordinating node merges / reduces
→ response
Shard 数量过多会放大 Scatter-Gather、协调和小文件成本。
Query、Filter 与 Aggregation
- Query Context:通常参与相关性评分。
- Filter Context:只判断是否匹配,适合状态、时间、权限等结构化条件。
- Aggregation:对匹配文档做桶分组、指标统计和分析。
权限过滤应在 Query 层强制执行,不能先返回跨租户结果再让应用“自行忽略”。
更新和删除为什么有成本
Segment 不可变,更新通常等价于“写新版本 + 标记旧文档删除”;旧数据空间要等 Segment Merge 才真正回收。
因此高频小更新、频繁 Refresh 和错误的文档粒度会增加 Merge、I/O 和磁盘放大。
业务事实同步模式
常见架构:
PostgreSQL / MySQL (Source of Truth)
→ Outbox / CDC / Kafka
→ Indexer
→ Elasticsearch (Search Projection)
当索引丢失或 Mapping 变更时,应能从事实层重建,而不是让业务只能依赖 ES 中的副本。
高频生产问题
| 问题 | 机制 | 治理方向 |
|---|---|---|
| Mapping Explosion | 动态字段无限增长 | 显式 Mapping、字段治理 |
| Too Many Shards | 每个 Shard 有固定成本 | 合理分片、Rollover / ILM |
| Hot Shard | Routing / 数据倾斜 | 调整 Key、Shard 和写入模型 |
| Deep Pagination | 大 Offset 需要各 Shard 保留大量候选 | search_after / PIT |
| Merge Pressure | 高频写入、删除、Refresh | Bulk、调 Refresh、容量规划 |
| Heap Pressure | Aggregation、Fielddata、Shard 过多 | Mapping、Query、节点与内存治理 |
| Unassigned Shard | 节点、磁盘、水位或分配规则 | 查 Allocation Explain |
观测重点
indexing / search latency and rate
rejected operations / thread pools
JVM heap / GC
segment count / merge time
primary and replica shard health
unassigned shards
refresh / flush / translog
node disk watermarks
document and mapping growth
适用与不适用
适合:全文搜索、日志检索、商品/内容搜索、复杂过滤和聚合、可重建派生索引。
不适合默认承担:强事务业务事实、复杂关系约束、高频跨文档原子更新、大对象存储。
面试掌握标准
- 倒排索引;
-
text、keyword与 Analyzer; - Segment、Refresh 与 NRT;
- Primary / Replica Shard;
- 分布式写入与 Scatter-Gather 查询;
- Mapping Explosion、Too Many Shards、Deep Pagination;
- ES 为什么通常是派生搜索层;
- 如何从数据库 + CDC 重建索引。
官方资料
- Index fundamentals: https://www.elastic.co/docs/manage-data/data-store/index-basics
- Mapping reference: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference
- Near real-time search: https://www.elastic.co/docs/manage-data/data-store/near-real-time-search