ElasticSearch #

分布式搜索与分析引擎,全文搜索的核心解决方案


📋 目录 #


核心原理 #

倒排索引 #

ES 速度快的秘密

文档

分词处理

倒排索引

快速检索

倒排索引结构:

词项 (Term) 文档列表 (Postings)
"苹果" [文档1, 文档2]
"水果" [文档1]
"公司" [文档2]
"产品" [文档2]

工作流程:

文档1: "苹果是一种水果"
文档2: "苹果公司推出了新产品"

↓ 分词处理

倒排索引:
"苹果" → [文档1, 文档2]
"水果" → [文档1]  
"公司" → [文档2]
"产品" → [文档2]

↓ 用户搜索 "苹果"

快速找到: 文档1, 文档2

分布式架构 #

客户端

协调节点

分片1

分片2

分片3

副本

副本

副本

核心概念:

概念 说明
分片 (Shard) 数据水平分片,提升并行度
副本 (Replica) 数据冗余,提升可用性和读并发
协调节点 路由请求,聚合结果

查询与评分 #

BM25 算法:

score = IDF × (TF / (TF + k1 × ((1 - b) + b × DL/ALG)))

其中:
  - IDF: 逆文档频率(词越稀有越重要)
  - TF: 词频(词出现次数越多越重要)
  - k1, b: 调节参数
  - DL: 文档长度
  - ALG: 平均文档长度

高可用机制 #

面试高频 ⭐⭐⭐⭐⭐ ES 如何保证高可用?节点挂了怎么办?

副本机制(基础高可用) #

每个主分片可以配置多个副本分片:

Primary Shard 0

Replica Shard 0

Primary Shard 1

Replica Shard 1

Primary Shard 2

Replica Shard 2

副本作用:

  1. 高可用:主分片挂了,副本提升为主分片继续服务
  2. 提升读性能:读请求可以负载均衡到主分片和副本分片

配置:

PUT /my_index/_settings
{
  "number_of_replicas": 1  // 每个主分片1个副本,总数据量翻倍
}

⚠️ 主分片和副本不会分配在同一个节点上,保证节点宕机副本还在

主节点选举 #

ES 集群通过 ZenDiscovery 模块选主:

选主流程(ES 7.x之前):

  1. 所有节点互相发现,互相ping
  2. 得到节点列表,按照节点id排序
  3. 第一个节点当选为主节点
  4. 超过半数节点同意当选,完成选举

脑裂问题: 网络分区时,可能选出多个主节点,导致集群分裂,数据不一致。

脑裂解决:

# 旧版配置
discovery.zen.minimum_master_nodes: (总节点数 / 2) + 1

必须获得超过半数节点选票才能当选,所以分区后只有一个分区能选出主节点。

ES 7.x之后改进:

  • 基于 Raft 算法 选主,不需要手动配置 minimum_master_nodes
  • 自动处理脑裂,更可靠

故障转移流程 #

当数据节点宕机:

  1. 主节点感知到节点失联(ping超时)
  2. 该节点上的所有主分片,在对应副本节点上提升为主分片
  3. 集群恢复黄/绿状态,继续服务

当主节点宕机:

  1. 剩余节点重新选举新主节点
  2. 新主节点重新分配分片,集群恢复

集群状态说明:

状态 说明
Green 所有主分片和副本都正常分配
Yellow 所有主分片正常分配,有副本未分配(单节点集群总是Yellow)
Red 有主分片未分配,集群部分不可用

分片分配感知 #

ES 主节点分配分片时,会感知节点拓扑

机架 → 节点 → 分片

尽量把主分片和副本分配到不同机架→ 即使整个机架掉电,副本还在其他机架,集群可用。

持久化机制 #

ES 通过 translog 保证数据不丢失:

  1. 数据写入内存buffer → 写入translog日志
  2. refresh 生成新segment → 清空buffer
  3. 如果节点宕机,重启时回放translog恢复内存中未flush的数据

持久化流程:

  • translog 同步刷盘,保证数据不丢
  • 定期flush将segment写入磁盘
  • 合并小segment为大segment

ES 高可用总结 #

层面 保障机制
数据层面 副本冗余,主分片宕机副本升级
节点层面 自动故障发现,自动故障转移
选举层面 过半机制,防止脑裂
拓扑层面 分片分配感知,跨机架分配
持久化层面 translog 日志,宕机可恢复

索引设计 #

索引创建 #

PUT /my_index
{
  "settings": {
    "number_of_shards": 3,          // 主分片数(不可修改)
    "number_of_replicas": 1,        // 副本数(可动态调整)
    "refresh_interval": "1s",       // 刷新间隔
    "index": {
      "max_result_window": 10000    // 深分页限制
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_max_word",  // 中文分词
        "fields": {
          "keyword": {              // 多字段:精确匹配
            "type": "keyword"
          }
        }
      },
      "price": { "type": "double" },
      "timestamp": { "type": "date" },
      "content": {
        "type": "text",
        "index": false              // 不索引(仅存储)
      }
    }
  }
}

动态映射 vs 显式映射 #

映射方式 优点 缺点
动态映射 使用方便,自动推断 可能类型冲突
显式映射 精确控制,性能更好 需要预定义

分片设计原则 #

总数据量 / 单分片建议容量(50GB) = 主分片数

例如: 500GB数据 → 10个主分片

注意事项:
  - 主分片数创建后不可修改
  - 过多分片增加集群开销
  - 过少分片限制扩展性

分词器选择 #

面试重点 ⭐⭐⭐⭐

分词器组成 #

分词器 = Character Filters + Tokenizer + Token Filters

字符过滤器 (Character Filters)
  ↓ 分词前预处理
分词器 (Tokenizer)  
  ↓ 词语切分
词条过滤器 (Token Filters)
  ↓ 后处理(小写、停用词、同义词等)

常用分词器对比 #

分词器 适用场景 特点
Standard 通用 国际化支持好
Simple 英文 非字母字符分隔
Whitespace 日志分析 保留格式
IK Analyzer 中文 ⭐ 粗粒度/细粒度
结巴分词 中文 未登录词识别弱
Snowball 英文 词干提取
Keyword 精确匹配 不分词

IK 分词器 #

# 安装
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.x.x/elasticsearch-analysis-ik-7.x.x.zip

# 测试
GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "中华人民共和国"
}
# 输出: ["中华人民共和国", "中华", "人民", "共和国"]

GET /_analyze
{
  "analyzer": "ik_smart",
  "text": "中华人民共和国"  
}
# 输出: ["中华人民共和国"]

同义词配置 #

"settings": {
  "analysis": {
    "filter": {
      "synonym_filter": {
        "type": "synonym",
        "synonyms_path": "analysis/synonyms.txt"
      }
    },
    "analyzer": {
      "my_synonym_analyzer": {
        "tokenizer": "standard",
        "filter": ["lowercase", "synonym_filter"]
      }
    }
  }
}

synonyms.txt:

手机, 电话, 移动电话
笔记本电脑, 笔记本, 本本

实战优化 #

性能优化 #

ES性能优化

写入优化

查询优化

存储优化

Bulk批量

调整refresh_interval

副本数设0写入期

合理使用分页

精确匹配用keyword

避免通配符

force merge

禁用_source

冷热分离

写入优化:

# 1. 使用Bulk API
POST /_bulk
{"index": {"_index": "logs"}}
{"field1": "value1", "field2": "value2"}
{"index": {"_index": "logs"}}
{"field1": "value3", "field2": "value4"}

# 2. 调整刷新间隔(写入高峰期)
PUT /my_index/_settings
{
  "index": {
    "refresh_interval": "30s"  # 默认1s
  }
}

# 3. 写入时关闭副本
PUT /my_index/_settings
{
  "number_of_replicas": 0
}

查询优化:

// 1. 使用filter代替query(不计算评分)
{
  "query": {
    "bool": {
      "filter": {
        "term": { "status": "active" }
      }
    }
  }
}

// 2. 分页限制(避免深分页)
GET /_search
{
  "from": 0,
  "size": 10,
  "query": { ... }
}

// 3. 使用scroll API(大批量)
GET /my_index/_search?scroll=1m
{
  "size": 1000,
  "query": { ... }
}

索引生命周期管理 (ILM) #

PUT _ilm/policy/logs_policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "30d"
          }
        }
      },
      "warm": {
        "min_age": "30d",
        "actions": {
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

冷热分离架构 #

热数据 Hot - SSD

实时写入

频繁查询

短期保留

温数据 Warm - SATA

偶尔查询

强制合并

中期保留

冷数据 Cold - 对象存储

归档数据

长期保留


🎯 面试题汇总 #

基础 #

  1. 什么是倒排索引?
  2. ES为什么搜索快?
  3. 什么是分片和副本?
  4. 如何选择分片数量?

分词 #

  1. 中文分词器有哪些?
  2. IK分词器的细粒度/智能模式区别?
  3. 如何配置同义词?
  4. 分词器组成部分?

优化 #

  1. 如何提高写入性能?
  2. 深分页问题及解决方案?
  3. 如何减少索引大小?
  4. 什么是ILM?

高级 #

  1. BM25评分算法?
  2. 分布式搜索流程?
  3. ES如何保证高可用?节点挂了怎么办?
  4. 脑裂问题及解决?
  5. 集群黄/红状态是什么意思?如何解决?
  6. 如何实现拼音搜索?

🎯 面试题答案详解 #

基础篇 #

  1. 什么是倒排索引?

答案:

倒排索引 = 词项 → 文档列表

正排 vs 倒排:

类型 说明
正排索引 文档 → 词项(已知文档找词)
倒排索引 词项 → 文档(已知词找文档)⭐

示例:

文档1: "苹果是一种水果"
文档2: "苹果公司推出新产品"

倒排索引:
苹果 → [文档1, 文档2]
水果 → [文档1]
公司 → [文档2]
产品 → [文档2]

  1. ES为什么搜索快?

答案:

技术 作用
倒排索引 按词项快速找到文档
分片并行 搜索分散到多个节点并行
Lucene内核 优化的索引结构,如FST
缓存机制 File System Cache

  1. 什么是分片和副本?

答案:

概念 作用 说明
Shard(分片) 水平分割数据 提高并行度
Replica(副本) 高可用+读扩展 主分片挂了副本升级

架构:

Index

Shard 0

Shard 1

Replica 0

Replica 1


  1. 如何选择分片数量?

答案:

经验公式:

分片数 = 总数据量 / 单分片推荐容量

单分片推荐: 20GB-50GB
太小 → 分片过多,开销大
太大 → 扩容困难

注意:

  • 分片数创建后不能修改
  • 但可以用 _reindex API重新索引

分词篇 #

  1. 中文分词器有哪些?

答案:

分词器 特点
IK Analyzer 最常用,ik_smart/ik_max_word
结巴分词 好用,速度快
HanLP 功能最全,支持NLP
Standard ES自带,按空格切,中文不行

  1. IK分词器的细粒度/智能模式区别?

答案:

模式 说明 示例: "中华人民共和国"
ik_max_word 细粒度,尽可能多切分 中华人民共和国,中华,人民,共和国
ik_smart 智能模式,粗粒度 中华人民共和国

使用场景:

  • 索引时用ik_max_word(分细一点,召回全)
  • 搜索时用ik_smart(分粗一点,更准确)

  1. 如何配置同义词?

答案:

步骤:

  1. 创建analysis/synonyms.txt文件
手机,电话,移动电话
笔记本电脑,笔记本,本本
  1. 索引配置
{
  "settings": {
    "analysis": {
      "filter": {
        "synonym_filter": {
          "type": "synonym",
          "synonyms_path": "analysis/synonyms.txt"
        }
      }
    }
  }
}

  1. 分词器组成部分?

答案:

分词器 = Character Filters + Tokenizer + Token Filters

Character Filters(字符过滤): 过滤HTML、转大写等
Tokenizer(分词): 切分词语
Token Filters(词过滤): 小写、同义词、停用词

优化篇 #

  1. 如何提高写入性能?

答案:

优化项 配置
批量写入 Bulk API
调大refresh_interval 默认1s → 30s或-1
副本数设0 写入时设0,写入完恢复
使用SSD 磁盘IO快
异步刷盘 translog.async

  1. 深分页问题及解决方案?

答案:

深分页问题: from = 1000000, size = 10 → 性能极差

解决方案:

方案 适用场景
Scroll 遍历大数据(导出)
Search After 分批查询,利用上一页最后一条
避免深分页 产品层面限制页数

Search After示例:

{
  "size": 10,
  "search_after": [last_sort_value], // 上一页最后一条
  "sort": [{"id": "asc"}]
}

  1. 如何减少索引大小?

答案:

方法 说明
禁用_source 不需要完整文档时禁用
压缩存储 index.codec: best_compression
字段类型优化 用keyword不用text(不需要分词)
force_merge 合并segment,清理删除标记

  1. 什么是ILM?

答案:

ILM = Index Lifecycle Management,索引生命周期管理

四个阶段:

Hot → Warm → Cold → Delete
热    温    冷     删除

配置示例:

{
  "policy": {
    "hot": {"actions": {"rollover": {"max_size": "50GB", "max_age": "30d"}}},
    "warm": {"min_age": "30d", "actions": {"allocate": {"number_of_replicas": 0}, "forcemerge": {"max_num_segments": 1}}},
    "cold": {"min_age": "60d", "actions": {"allocate": {"number_of_replicas": 0}}},
    "delete": {"min_age": "90d", "actions": {"delete": {}}}
  }
}

高级篇 #

  1. BM25评分算法?

答案:

BM25公式:

score = Σ( IDF * TF / (TF + k1 * (1 - b + b * (fieldLength / avgFieldLength))) )

核心概念:

参数 含义
IDF 逆文档频率,词越稀有越重要
TF 词频,词出现次数越多越重要
fieldLength 字段长度,越短越重要
k1, b 调节参数

  1. 分布式搜索流程?

答案:

Client

协调节点

Shard0

Shard1

Shard2

Query Phase

协调节点合并

Fetch Phase

返回结果

两个阶段:

  1. Query Phase: 每个分片返回TopK doc ID
  2. Fetch Phase: 协调节点合并排序,获取完整文档

  1. ES如何保证高可用?节点挂了怎么办?

答案:

高可用机制:

机制 作用
副本机制 主分片挂了副本升级
自动分片重分配 节点挂了自动把分片移到其他节点
脑裂预防 discovery.zen.minimum_master_nodes

节点挂了流程:

1. 主节点检测到某节点失联
2. 该节点上的主分片 → 选一个副本升级为主
3. 重新分配缺失的副本
4. 集群恢复Green/Yellow状态

  1. 脑裂问题及解决?

答案:

脑裂: 网络分区时,多个节点认为自己是主节点

解决方法:

ES版本 配置
7.x前 discovery.zen.minimum_master_nodes: (n/2)+1
7.x+ 自动,无需配置

原理:必须获得超过半数节点同意才能成为主


  1. 集群黄/红状态是什么意思?如何解决?

答案:

状态 说明
Green 所有主分片和副本都正常 ✓
Yellow 所有主分片正常,有副本异常
Red 有主分片异常!

Yellow解决:

GET /_cat/indices?v
# 查看哪些索引副本分配失败
# 增加节点或调整副本数
PUT /index/_settings
{"number_of_replicas": 0}

Red解决:

# 紧急!检查主分片为什么分配失败
GET /_cluster/allocation/explain
# 可能原因: 节点不够、磁盘满、分片丢失

  1. 如何实现拼音搜索?

答案:

方案1: pinyin分词器

{
  "settings": {
    "analysis": {
      "analyzer": {
        "pinyin_analyzer": {
          "tokenizer": "pinyin_tokenizer"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "ik_max_word",
        "fields": {
          "pinyin": {
            "type": "text",
            "analyzer": "pinyin_analyzer"
          }
        }
      }
    }
  }
}

搜索:

{
  "query": {
    "multi_match": {
      "query": "zhongguo",
      "fields": ["name", "name.pinyin"]
    }
  }
}

🔗 相关笔记 #

  • MySQL
  • 消息队列
  • 分布式系统

最后更新: 2026-04-29