缓存03:Elasticsearch

从倒排索引原理、分片路由、聚合查询到性能调优,拆解 Elasticsearch 在全文检索和分析场景下的核心机制和常见坑点。

字数 1847 阅读时长 ≈ 6 分钟 2026-7-13 2026-7-13
缓存03:Elasticsearch

Elasticsearch 不是”数据库”,而是”搜索引擎 + 分析引擎”。它的核心场景是全文检索、日志分析、监控指标和聚合查询,支持倒排索引、分片路由、分布式聚合和实时搜索,适合搜索服务、日志平台、监控系统和数据分析场景。

这篇文章围绕四个核心机制:倒排索引、分片路由、聚合查询、性能调优,把 Elasticsearch 的设计逻辑和常见坑点讲清楚。

为什么选 Elasticsearch,而不是 MySQL LIKE

先定位清楚:Elasticsearch 的核心场景是全文检索和聚合分析,支持倒排索引、分词、相关性排序和分布式聚合,适合搜索服务、日志分析、监控指标。

与 MySQL LIKE 查询的差异:

维度ElasticsearchMySQL LIKE
检索方式倒排索引,直接定位文档全表扫描,逐行匹配
分词能力内置分词器,支持中文分词不支持分词,模糊匹配
相关性排序TF-IDF / BM25 算法,按相关性排序不支持,按主键排序
聚合能力分布式聚合,支持多维分析GROUP BY,单机聚合
写入性能批量写入,性能高单条写入,事务开销大
事务支持不支持事务,最终一致性ACID 事务,强一致性

简单判断:全文检索、日志分析、多维聚合 → Elasticsearch 合适。事务操作、主键查询、强一致性 → MySQL 合适。

Elasticsearch 不是 MySQL 的替代品,而是 MySQL 的补充:MySQL 存储数据,Elasticsearch 建立搜索索引,两者配合使用。

倒排索引:搜索的核心

Elasticsearch 的检索能力来自倒排索引(Inverted Index),这是搜索引擎的核心数据结构。

正排索引 vs 倒排索引

  • 正排索引:文档 ID → 文档内容,适合按 ID 查询文档
  • 倒排索引:词(Term)→ 文档 ID 列表,适合按词查询文档

示例:

文档 1:Elasticsearch 是搜索引擎
文档 2:MySQL 是数据库
文档 3:搜索引擎和数据库都是中间件

正排索引:
文档 1 → Elasticsearch, 是, 搜索引擎
文档 2 → MySQL, 是, 数据库
文档 3 → 搜索引擎, 和, 数据库, 都, 是, 中间件

倒排索引:
Elasticsearch → 文档 1
是 → 文档 1, 文档 2, 文档 3
搜索引擎 → 文档 1, 文档 3
MySQL → 文档 2
数据库 → 文档 2, 文档 3
和 → 文档 3
都 → 文档 3
中间件 → 文档 3

搜索”搜索引擎”时,直接从倒排索引找到文档 1 和文档 3,不需要扫描所有文档。

分词:倒排索引的前提

文档写入 Elasticsearch 前,需要分词(Analysis):

  • 字符过滤:去除 HTML 标签、特殊字符
  • 分词:将文本拆成词(Token),如”搜索引擎”拆成”搜索”、“引擎”
  • 词过滤:去除停用词(如”的”、“是”)、小写化、同义词转换

Elasticsearch 内置分词器:

分词器说明适用场景
standard按空格和标点分词,多语言通用通用场景
simple按字母分词,去除非字母英文简单分词
whitespace按空格分词不需要复杂分词
ik_max_word / ik_smart中文分词插件中文场景

中文场景推荐安装 IK 分词器,支持中文智能分词。

相关性排序:TF-IDF / BM25

搜索结果按相关性排序,Elasticsearch 使用 TF-IDF(7.x 之前)或 BM25(7.x 之后)算法:

  • TF(Term Frequency):词频,词在文档中出现次数越多,相关性越高
  • IDF(Inverse Document Frequency):逆文档频率,词在所有文档中出现越少,区分度越高
  • BM25:改进的 TF-IDF,避免长文档权重过高

简单理解:关键词在文档中出现次数多、在其他文档中出现少,相关性高。

分片路由:分布式检索的基础

Elasticsearch 是分布式系统,数据分片存储在多个节点上。

分片类型

分片类型说明
主分片(Primary Shard)存储数据,参与读写,一个索引可有多个主分片
副本分片(Replica Shard)主分片的副本,分担读请求,提供高可用

分片数量在创建索引时指定,后续不能修改(需重建索引):

PUT /my_index
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}

分片路由规则

文档写入时,根据路由规则分配到主分片:

shard_num = hash(_routing) % num_primary_shards

默认 _routing 是文档 ID,可以自定义路由字段。相同路由字段的文档会分配到同一分片,适合关联数据(如同一订单的日志)。

分片数量怎么定

分片数量影响并发度和资源消耗:

  • 分片过少:并发度低,搜索慢
  • 分片过多:资源消耗大(每个分片是一个 Lucene 实例),聚合慢

经验值:每个分片大小 10GB - 50GB,每个节点分片数量不超过 20 个。根据数据量估算:

分片数量 = 数据总量 / 单分片大小(如 30GB)

聚合查询:多维分析的核心

Elasticsearch 的聚合能力是它区别于其他搜索引擎的核心能力,支持多维分析、统计和可视化。

聚合类型

聚合类型说明示例
Metric 聚合计算指标,如 sum、avg、max、min订单总金额、平均价格
Bucket 聚合分桶聚合,如 terms、range、histogram按用户分组、按时间分桶
Pipeline 聚合对聚合结果再聚合,如 derivative、cumulative_sum计算增长率、累计求和

示例:按用户分组统计订单金额

GET /orders/_search
{
  "size": 0,
  "aggs": {
    "group_by_user": {
      "terms": { "field": "user_id", "size": 10 },
      "aggs": {
        "total_amount": { "sum": { "field": "amount" } }
      }
    }
  }
}

聚合的坑点

坑点说明解决方式
分片聚合 + 合并每个分片先聚合,再合并结果,可能导致 terms 聚合结果不准确增加 shard_size 参数,每个分片返回更多桶
聚合字段未建立 doc_values聚合依赖 doc_values,字段未建立则聚合失败字段映射设置 "doc_values": true
text 字段聚合text 字段默认不支持聚合,需要 fielddata使用 keyword 子字段或启用 fielddata(不推荐)

性能调优:写入和查询

Elasticsearch 的性能调优围绕写入和查询两个维度。

写入调优

优化点说明
批量写入使用 Bulk API 批量写入,减少网络开销
刷新间隔增大 refresh_interval(默认 1s),减少刷新频率,提升写入性能
副本数量写入时设置 number_of_replicas=0,写入完成后恢复,减少副本同步开销
禁用刷新大批量写入时禁用刷新 refresh=false,写入完成后手动刷新

配置示例:

PUT /my_index/_settings
{
  "index": {
    "refresh_interval": "30s",
    "number_of_replicas": 0
  }
}

查询调优

优化点说明
避免通配符查询*keyword* 会导致全表扫描,性能差
使用 filter 上下文filter 不计算评分,会缓存结果,性能比 query 高
分页深度深分页(from + size 过大)性能差,使用 search_after 替代
字段映射优化不需要搜索的字段设置 "index": false,减少索引体积

filter 上下文示例:

GET /orders/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "paid" } },
        { "range": { "amount": { "gte": 100 } } }
      ]
    }
  }
}

常见误区

误区真实情况
Elasticsearch 是数据库它是搜索引擎,不支持事务,不适合作为主存储
分片越多越好分片过多资源消耗大,聚合慢
text 字段可以直接聚合text 字段默认不支持聚合,需要 keyword 子字段
深分页可以用 from + size深分页性能差,需要用 search_after 或 scroll

项目判断

引入 Elasticsearch 时,先回答三个问题:

  1. 检索场景:是否需要全文检索、模糊匹配、相关性排序?
  2. 聚合场景:是否需要多维聚合、统计分析、可视化?
  3. 数据量:数据量是否达到百万级以上?

如果需要全文检索、多维聚合、数据量大 → Elasticsearch 合适。如果只是简单查询、事务操作、数据量小 → MySQL 合适。

线上排查 Elasticsearch 问题:

  • 慢查询:使用 _search?profile=true 分析查询耗时,检查是否通配符查询、深分页
  • 写入慢:检查刷新间隔、副本数量,使用 Bulk API 批量写入
  • 磁盘满:清理旧数据、调整保留策略、扩容磁盘
  • 分片不均衡:检查分片路由规则,手动重新分配分片