Skip to content

十一、ElasticSearch(6 题)

11.1 ★★★★ElasticSearch 的核心概念有哪些?Index、Document、Shard、Replica 之间的关系是怎样的?集群的节点角色是什么?

📖 点击查看答案

核心概念

  • Index:类似数据库的表,是 Document 的逻辑集合。
  • Type:7.x 已废弃,6.x 仅支持 _doc
  • Document:最小数据单元,JSON 格式,有唯一 _id
  • Field:文档中的字段,类似数据库列。
  • Mapping:定义字段类型(text/keyword/integer/date 等)与分词规则。
  • Setting:索引配置(分片数、副本数、刷新间隔等)。

关系层级

  • Cluster → Node → Index → Shard → Segment → Document
  • Index 逻辑包含多个 Shard(主分片),每个 Shard 可有 Replica(副本分片)。
  • Shard 是 Lucene 实例,包含多个 Segment(不可变段)。
  • 一个 Document 属于一个 Shard,由 _routing(默认 _id hash)决定。

节点角色

角色职责适用规模
Master-eligible管理集群元数据、索引创建/删除3 个(防脑裂)
Data存储 Shard,执行 CRUD/搜索可水平扩展
Coordinating路由请求、汇总结果(默认所有节点)高查询场景独立部署
Ingest文档预处理(pipeline)数据清洗
Machine Learning异常检测(付费)安全监控

分片与副本

  • Primary Shard:主分片,索引创建时固定(7.x 默认 1),需 reindex 才能改。
  • Replica Shard:副本分片,可动态调整,提供高可用与读扩展。
  • 主副本不可在同一节点(保证容灾)。
json
// 索引创建
PUT /orders
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "orderId": { "type": "keyword" },
      "amount": { "type": "double" },
      "createTime": { "type": "date" }
    }
  }
}

关键点:分片数 = 主分片数 × (1 + 副本数);分片过小限制容量,过大浪费资源(建议单分片 30-50GB);Master 节点不存储数据,专用保证稳定。


11.2 ★★★★倒排索引的原理是什么?Term Dictionary、Posting List 分别起什么作用?为什么要用倒排索引?和 MySQL 正向索引有什么区别?

📖 点击查看答案

倒排索引原理:将文档中的词项(Term)提取出来,建立 Term → 文档 ID 列表的映射,搜索时通过词项直接定位文档,避免全表扫描。

核心组件

  • Term Dictionary:存储所有去重后的词项,按字典序排序,支持二分查找。
  • Term Index:Term Dictionary 的索引(FST 数据结构),常驻内存,快速定位 Term 在 Dictionary 中的位置。
  • Posting List:每个 Term 对应的文档 ID 列表,存储 doc_id、词频(TF)、位置(Position)、偏移(Offset)。
  • Segment:不可变的倒排索引单元,多个 Segment 合并(Merge)提升查询效率。

Posting List 压缩

  • FOR(Frame of Reference):对有序 doc_id 增量编码 + 位压缩。
  • Roaring Bitmap:用于 filter 场景的位图压缩,支持高效集合运算(AND/OR)。

为什么用倒排索引

  • 全文检索场景下,正向索引需扫描所有文档匹配关键词,O(N) 复杂度。
  • 倒排索引通过词项直接拿到文档列表,O(1) ~ O(log N) 复杂度。
  • 支持短语查询(Position)、相关性打分(TF-IDF/BM25)。

与 MySQL 正向索引对比

维度MySQL 正向索引ES 倒排索引
结构B+Tree,行 → 字段值Term → Doc 列表
查询按主键/范围扫描按词项直达文档
全文搜索LIKE '%xxx%' 全表扫分词后查 Term
聚合需扫描所有行直接遍历 Posting List
更新原地更新(B+Tree)不可变 Segment + 标记删除
适用等值/范围查询、事务全文检索、多维聚合

示例

文档1: "Elasticsearch is fast"
文档2: "Elasticsearch is distributed"

Term Dictionary:
  distributed → [2]
  elasticsearch → [1, 2]
  fast → [1]
  is → [1, 2]

查询 "elasticsearch fast":
  倒排:elasticsearch ∩ fast = {1,2} ∩ {1} = {1}

关键点:分词器(Analyzer)决定 Term 的生成;Keyword 类型不分词(整体作为一个 Term),Text 类型分词;MySQL 适合结构化查询,ES 适合非结构化检索。


11.3 ★★★★ElasticSearch 的写入流程是怎样的?数据是如何从 Coordinator Node 到 Primary Shard 再到 Replica Shard 的?近实时搜索的原理(refresh、flush、translog)是什么?

📖 点击查看答案

写入流程

  1. Coordinator Node 接收写请求,根据 _routing(默认 _id)计算目标 Shard。
  2. Primary Shard 接收请求,写入 translog + Index Buffer。
  3. Primary 将请求并行转发给所有 Replica Shard
  4. Replica 写入成功后返回 ACK 给 Primary。
  5. Primary 等待所有 Replica(或 wait_for_active_shards 指定数量)ACK 后,返回给 Coordinator。
  6. Coordinator 返回客户端成功。

近实时搜索原理

  • Index Buffer:写入先进入内存缓冲区,默认 1 秒后触发 refresh。
  • refresh:将 Index Buffer 生成新 Segment,写入 OS PageCache(文件系统缓存),此时可被搜索。默认 1 秒,所以称"近实时"。
  • translog:写入时同步记录日志(类似 MySQL redo log),保证宕机不丢数据。index.translog.durabilityrequest(每次请求 fsync,安全但慢)或 async(默认 5s fsync)。
  • flush:将 PageCache 中的 Segment 刷盘(fsync)+ 清空 translog。默认触发条件:translog 达 512MB 或 30 分钟。
写入 → Index Buffer + translog
       ↓ refresh(1s)
       Segment(PageCache,可搜索)
       ↓ flush(512MB / 30min)
       Segment 持久化到磁盘 + 清空 translog

Segment Merge

  • Segment 不可变,频繁 refresh 产生大量小 Segment。
  • 后台线程 Merge 小 Segment 为大 Segment,物理删除被标记的文档。
  • 减少文件句柄与查询时合并开销。
json
// 调整刷新间隔(写入密集场景)
PUT /orders/_settings
{
  "index.refresh_interval": "30s",  // 默认 1s
  "index.translog.durability": "async",
  "index.translog.sync_interval": "30s"
}

写一致性与 quorum

  • wait_for_active_shards:默认 1(仅 Primary),可设 allquorum
  • 写入失败时 Coordinator 会重试到其他 Replica。

关键点:refresh 是"可搜索"的门槛,flush 是"持久化"的门槛;translog 是数据安全兜底;批量写入时增大 refresh_interval 与 buffer 可大幅提升吞吐。


11.4 ★★★ElasticSearch 如何实现深度分页?scrollsearch_after 的区别是什么?from/size 为什么有性能问题?

📖 点击答案

from/size 的性能问题

  • 分页公式:from + size,每个 Shard 返回 from + size 条文档给 Coordinator。
  • Coordinator 需对 shard数 × (from + size) 条数据排序后取 size 条。
  • 默认限制 from + size ≤ 10000,深度分页时内存与 CPU 开销巨大。

三种深度分页方案

  1. scroll:基于快照,首次查询生成 scroll_id,后续携带 scroll_id 拉取下一批。

    • 适合全量导出/重计算,不适合实时查询(快照过期)。
    • scroll=1m 指定快照保留时间,过期自动清理。
  2. search_after:基于排序字段的游标,每次查询携带上一页最后一条的排序值。

    • 实时查询,无快照开销,适合用户分页。
    • 要求排序字段唯一(建议 _id 兜底)。
  3. search_after + PIT:7.10+ 推荐方案,结合 Point In Time 保证数据一致性。

对比

维度from/sizescrollsearch_after
场景浅分页全量导出深度分页/实时
一致性实时快照实时(需排序字段稳定)
性能深度差稳定稳定
限制≤ 10000需维护 scroll_id需排序字段唯一
json
// scroll 示例
POST /orders/_search?scroll=1m
{
  "size": 100,
  "query": { "match_all": {} }
}
// 后续
POST /_search/scroll
{
  "scroll": "1m",
  "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAA..."
}

// search_after 示例
GET /orders/_search
{
  "size": 10,
  "sort": [
    { "createTime": "desc" },
    { "_id": "asc" }  // 兜底保证唯一
  ]
}
// 下一页
GET /orders/_search
{
  "size": 10,
  "sort": [{ "createTime": "desc" }, { "_id": "asc" }],
  "search_after": ["2024-01-01", "abc123"]  // 上一页最后一条的排序值
}

关键点:scroll 占用资源大,需及时 clear_scroll 释放;search_after 不能跳页,只能顺序翻;用户分页推荐 search_after,导出场景用 scroll。


11.5 ★★★ElasticSearch 的聚合查询(Aggregation)有哪几种?Bucket、Metric、Pipeline 的区别?聚合查询如何优化?

📖 点击查看答案

聚合类型

  1. Bucket Aggregation(桶聚合):按规则将文档分组,类似 SQL GROUP BY

    • terms:按字段值分桶。
    • range:按数值范围分桶。
    • date_histogram:按时间分桶(如每天/每月)。
    • filter/filters:按条件分桶。
  2. Metric Aggregation(指标聚合):计算数值指标,类似 SQL SUM/AVG

    • 单值:avgsummaxmincardinality(去重数)、value_count
    • 多值:stats(一组统计值)、percentiles(分位数)、extended_stats
  3. Pipeline Aggregation(管道聚合):基于其他聚合结果二次计算。

    • cumulative_sum:累计求和。
    • derivative:求导(环比)。
    • bucket_sort:桶排序。
json
// 综合示例:按日期分桶 + 每桶求平均金额 + 累计求和
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "daily_sales": {
      "date_histogram": {
        "field": "createTime",
        "calendar_interval": "day"
      },
      "aggs": {
        "avg_amount": { "avg": { "field": "amount" } },
        "cumulative": {
          "cumulative_sum": { "buckets_path": "avg_amount" }
        }
      }
    }
  }
}

优化策略

  1. 加 query 过滤:聚合前用 query 缩小文档集,避免全量扫描。
  2. 使用 doc_values:默认开启,确保聚合字段非 fielddata(text 类型聚合慢)。
  3. keyword 而非 text:分桶字段用 keyword 类型,避免分词。
  4. size 控制terms 聚合 size 不要过大,避免桶爆炸。
  5. partition:terms 桶数过大时使用 include 分区聚合。
  6. 预计算:高频聚合场景用 Transform 或 Rollup 预聚合。
  7. 冷热分离:历史数据查得少,热数据 SSD 加速。

关键点:聚合在 Coordinator 汇总,深度嵌套聚合会放大请求量;cardinality 用 HyperLogLog 算法,有 1% 误差;fielddata 加载到堆内存,慎用。


11.6 ★★★ElasticSearch 集群的健康状态(Green/Yellow/Red)分别代表什么?集群脑裂问题是什么?如何进行性能优化?

📖 点击查看答案

集群健康状态

  • Green:所有主分片与副本分片都正常分配。
  • Yellow:主分片正常,部分副本未分配(如单节点时副本无法分配)。
  • Red:部分主分片不可用,数据丢失风险,需立即处理。

脑裂问题

  • 原因:网络分区导致部分 Master-eligible 节点失联,各自选举新 Master,集群分裂为两个独立集群。
  • 后果:两个 Master 同时写入,数据不一致,恢复时需手动合并。
  • ES 7.x+ 解决方案:
    • 自动选举机制:仅当 Master-eligible 节点数 ≥ discovery.zen.minimum_master_nodes(旧版需手动设置)时才能选举。
    • 7.x 默认 cluster.initial_master_nodes 启动引导,运行时基于投票 quorum = (master_eligible / 2) + 1
    • 推荐 3 个专用 Master 节点,避免脑裂。

性能优化

  1. 写入优化

    • 增大 refresh_interval(如 30s)减少 Segment 数量。
    • 批量写入(bulk API),单批 5-15MB。
    • 关闭副本先写入,完成后再开(number_of_replicas: 0)。
    • translog.durability=async 降低 fsync 频率。
  2. 查询优化

    • 用 filter 替代 query(filter 走缓存,不计算打分)。
    • 避免大 from 深度分页,用 search_after
    • 字段类型合理:精确匹配用 keyword,全文检索用 text。
    • 聚合字段开启 doc_values,关闭不需要的 _source
  3. 集群层面

    • 分片容量控制:单分片 30-50GB。
    • 节点堆内存 ≤ 32GB(指针压缩),且 ≤ 物理内存 50%。
    • 冷热分离:SSD 用于热数据,HDD 用于冷数据。
    • 预留磁盘水位:85% 告警,90% 只读,95% 限制分配。
yaml
# elasticsearch.yml 关键配置
cluster.name: my-cluster
node.roles: [data, master]  # 7.9+ 角色配置
indices.fielddata.cache.size: 40%
indices.queries.cache.size: 10%
thread_pool.search.queue_size: 1000
  1. JVM 优化
    • 堆内存设为物理内存 50%,不超过 32GB。
    • 使用 G1GC(JDK 11+)或 CMS。
    • 关闭 swap(bootstrap.memory_lock: true)。

关键点:监控指标(CPU、堆使用率、GC、查询延迟);慢查询日志开启(indices.search.slowlog);定期 _forcemerge 历史索引(只读后合并为 1 个 Segment)。

基于 4 份主流面试题库整合精选,共 200 题