微服务学习最终篇: Elasticsearch

微服务学习最终篇:Elasticsearch 从入门到生产实战

Elasticsearch(简称 ES)是一个高性能的分布式搜索引擎。它基于 Lucene 构建,支持分布式、可水平扩展,并提供 Restful 接口,可被任何编程语言调用。ELK 技术栈(Elasticsearch + Logstash + Kibana + Beats)已成为日志分析、实时监控领域的行业标配。

全文目录:

  1. 倒排索引 ------ ES 为什么这么快
  2. 核心概念:索引、文档、映射
  3. IK 分词器 ------ 中文搜索的救星
  4. 索引库与文档操作
  5. RestClient 系列 ------ Java 客户端怎么选
  6. 聚合分析 ------ 为什么需要数据聚合
  7. 分布式架构与集群高可用
  8. 读写流程原理
  9. 生产环境性能优化(加分项)
  10. 面试高频题汇总

一、倒排索引 ------ ES 为什么这么快

1.1 什么是倒排索引

Elasticsearch 之所以能在大数据量下实现毫秒级的全文搜索,核心秘密就在于它的底层数据结构------倒排索引(Inverted Index) 。

在传统关系型数据库(如 MySQL)中,搜索文本字段通常用 LIKE %keyword%,需要扫描每一行记录,效率极低。倒排索引的设计思路完全不同------它不存储"哪个文档包含了什么内容",而是存储 "这个关键词出现在了哪些文档中" 。

正排索引 vs 倒排索引:

  • 正排索引(Forward Index) :以文档为核心,文档 ID → 文档内容。这是存储原始数据的方式。
  • 倒排索引(Inverted Index) :以词(Term)为核心,关键词 → 包含该词的文档 ID 列表。这是用来搜索的方式。

用书籍来类比:

  • 正排索引就像书的目录(章节 → 页码),你知道第一章讲什么,但不知道"并发"这个词出现在哪几页。
  • 倒排索引就像书尾的索引页(关键词 → 页码列表),你可以直接找到"并发"出现在第 15、28、99 页。

1.2 核心结构

倒排索引由两部分组成:

组成部分 说明
词项字典(Term Dictionary) 所有不重复词的有序列表
倒排列表(Posting List) 每个词对应的文档 ID 列表(还包含位置、频率等信息)

举例:假设有三条商品数据:

  • Doc 1:红富士苹果
  • Doc 2:新鲜的香蕉
  • Doc 3:苹果和香蕉

构建倒排索引后:

Term(词项) Posting List(文档 ID 列表)
苹果 1, 3
红富士 1
香蕉 2, 3
新鲜 2

当搜索"苹果"时,ES 直接定位到倒排索引中的"苹果"一行,立即得到文档 ID 1, 3,无需遍历所有数据。

1.3 倒排索引的构建流程

倒排索引的构建需要经过分词(Tokenization) 和归一化(Normalization) 等处理:

  1. 字符过滤:去除 HTML 标签等无关字符
  2. 分词(Tokenization) :将文本拆分为词语,如 "Hello World" → ["Hello", "World"]
  3. 归一化 :转小写(FUN → fun)、去除停用词(is、the)、词干提取(running → run)

Elasticsearch 默认使用 standard 分析器,也支持自定义分析器(如 IK 中文分词器)。

1.4 Lucene 底层存储

每个 ES 分片底层对应一个 Lucene 索引实例。倒排索引在 Lucene 中由多个文件组成:

  • .tim 文件:存储词典(Term Dictionary),使用 FST 压缩存储,支持高效前缀查询
  • .doc 文件:存储 Posting List(文档 ID、词频等)
  • .pos 文件:存储词项在文档中的位置(用于短语查询)
  • .pay 文件:存储额外负载信息

二、核心概念:索引、文档、映射

2.1 索引(Index)

索引是存储相关数据的逻辑容器,类似于关系型数据库中的"数据库"或"表"。它是一组具有相似特征的文档集合。

⚠️ 注意:ES 中"索引"一词有双重含义:

  • 名词 :数据的逻辑存储单元(如 order_index)
  • 动词:将文档写入 ES 的过程(如 "index a document")

ES 存储采用 JSON 格式,与 MongoDB 类似。

2.2 文档(Document)

文档是 ES 中可被索引的基本数据单元,采用 JSON 格式表示。每个文档属于一个索引,包含多个字段(Field)。

文档是无模式(schema-less) 的,但生产环境通常通过映射来定义结构以保证一致性。

2.3 映射(Mapping)------ 面试必考

映射是定义索引中文档结构的元数据,相当于数据库中的"表结构" 。它规定了每个字段的类型(如 text、keyword、date)、是否分词、是否可被搜索等属性。

🔥 面试高频题:text 和 keyword 有什么区别?

这是几乎所有 ES 面试必问的一道题。

维度 text keyword
是否分词 ✅ 是 ❌ 否
支持全文检索 ✅ match 查询 ❌ 只能 term 精确匹配
支持聚合 ❌(需开启 fielddata,易 OOM) ✅ 原生支持
支持排序 ❌(同上) ✅
存储开销 高(倒排索引 + norms) 低

生产建议:

  • 需要全文搜索 的字段 → text(如文章内容、商品描述)
  • 需要精确匹配、聚合、排序 的字段 → keyword(如状态、标签、用户 ID)
⚠️ 生产警告:不要依赖动态映射

ES 默认支持动态映射(Dynamic Mapping) :插入新字段时自动推断类型。

但这份"智能"会埋下隐患:

  • user_id 一开始是数字(如 "12345"),被识别为 long
  • 后来第三方传来 UUID(如 "abc-def"),直接写入失败------类型冲突!

生产环境原则:永远不要依赖自动映射,必须显式定义 Mapping。

三、IK 分词器 ------ 中文搜索的救星

3.1 为什么需要 IK 分词器

ES 默认的分词器(如 Standard Analyzer)对中文支持较差,会将中文词语拆分为单个汉字(如"华为手机"拆分为"华""为""手""机"),无法满足中文全文检索需求。

IK 分词器是专门为中文设计的分词器,支持自定义词典,能准确拆分中文词语。

3.2 安装与使用

安装(版本必须与 ES 版本完全一致):

bash 复制代码
# 进入 ES 插件目录,下载对应版本的 IK 分词器
# https://github.com/medcl/elasticsearch-analysis-ik/releases
# 解压到 plugins/ik 目录,重启 ES

两种分词模式:

  • ik_max_word:最大粒度分词,将文本拆分为尽可能多的词语(索引时用)
  • ik_smart:最小粒度分词,将文本拆分为最合理的词语(搜索时用)

测试分词:

json 复制代码
POST /_analyze
{
  "analyzer": "ik_max_word",
  "text": "华为Mate 60 Pro手机"
}

3.3 自定义词典(生产必备)

编辑 plugins/ik/config/custom.dic 文件,添加自定义词语(如品牌名、专业术语),IK 分词器会优先识别这些词。

四、索引库与文档操作

4.1 索引库 CRUD

创建索引并定义 Mapping:

json 复制代码
PUT /product_index
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "title": { "type": "text", "analyzer": "ik_max_word" },
      "brand": { "type": "keyword" },
      "price": { "type": "float" },
      "created_at": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" }
    }
  }
}

常用 Mapping 属性:

  • type:字段类型(text、keyword、long、date 等)
  • analyzer:指定分词器
  • index:是否可被搜索(true/false)
  • doc_values:是否支持聚合排序(默认开启,text 类型默认关闭)

4.2 文档 CRUD

  • 新增/覆盖 :POST /index/_doc/id 或 PUT /index/_doc/id
  • 查询 :GET /index/_doc/id
  • 更新 :POST /index/_update/id(局部更新)
  • 删除 :DELETE /index/_doc/id
  • 批量操作 :POST /_bulk

五、RestClient 系列 ------ Java 客户端怎么选

5.1 三个客户端的演进

客户端 状态 说明
TransportClient ❌ 已废弃 早期客户端,使用 TCP 协议
RestClient(低级) ✅ 可用 纯 HTTP 通信,无 ES API 封装
RestHighLevelClient ⚠️ 7.15 弃用,8.0 移除 基于 RestClient 封装,提供 ES API
Java API Client(新) ✅ 官方推荐 全新设计,零 Server 依赖

5.2 RestClient vs RestHighLevelClient

对比维度 RestClient(低级) RestHighLevelClient(高级)【已弃用】
封装程度 纯 HTTP 工具 完整 ES API 封装
请求方式 手动拼接 JSON、URL 链式 API,自动构建
版本要求 宽松,跨版本兼容 必须与 ES 版本一致
代码量 大,重复代码多 简洁,易维护
适用场景 特殊接口、调试 曾是 90% 业务首选

5.3 新项目怎么选?

直接使用 Elasticsearch Java API Client(官方推荐)。它:

  • 与 ES Server 代码零耦合,纯 REST 协议
  • API 更一致、更易用
  • 是未来长期维护的方向

迁移时需要重写代码(非兼容升级)。

六、聚合分析 ------ 为什么需要数据聚合

6.1 什么是聚合?

如果说查询是"找数据",那么聚合就是 "看趋势" 。聚合分析是 ES 的核心进阶功能,本质是"对检索结果或全量数据进行分组、统计、计算"。

为什么需要聚合?

  • 日志分析:统计各状态码的请求量
  • 电商报表:按品牌统计商品平均价格
  • 用户画像:按年龄段、地域分组统计

ES 的聚合基于倒排索引和 doc_values 实现,具备高并发、低延迟的特性,适合实时分析场景。

6.2 聚合的三大分类

类型 功能 类比 SQL
Metric(指标) 计算数值指标(avg、sum、min、max、cardinality) SELECT AVG(price)
Bucket(分桶) 将文档分组(按日期、城市、状态) GROUP BY city
Pipeline(管道) 对其他聚合结果进行二次计算 窗口函数或子查询

常见 Bucket 聚合 :terms(最常用)、range、date_histogram

常见 Metric 聚合 :avg、sum、min、max、cardinality(去重计数)

6.3 面试必问:terms 聚合为什么是近似结果?

terms 聚合默认是近似结果,不是绝对精确。因为:

  1. 每个 shard 先取 top N
  2. 协调节点再合并各 shard 的结果

如果某个词在单个 shard 中未进入 top N,但在全局范围内应进入 top N,就会被遗漏。

解决方案 :调大 shard_size 参数(shard_size > size),牺牲性能换精度。

6.4 聚合执行流程

  1. Query 阶段:bool/filter 先筛选命中文档
  2. 每个 shard 本地执行聚合:分桶 + 计算指标
  3. 协调节点 merge:合并各 shard 的 bucket
  4. 如有 Pipeline 聚合:在协调节点对 bucket 再做一次计算

⚠️ 聚合 = CPU + 内存密集操作。shard 越多、bucket 越多,代价越高。

七、分布式架构与集群高可用

7.1 核心概念

ES 采用分布式架构,由多个 Node 组成 Cluster。每个索引被切分为多个 分片(Shard) 。

概念 说明
Cluster(集群) 多个节点组成的整体
Node(节点) 集群中的一个 ES 实例
Index(索引) 逻辑数据容器
Shard(分片) 索引的物理切分单元,每个分片是一个独立的 Lucene 实例
Replica(副本) 主分片的完整拷贝

7.2 节点角色

节点类型 核心职责
Master Node(主节点) 管理集群状态、索引创建/删除、分片分配
Data Node(数据节点) 存储数据分片,执行数据读写
Coordinating Node(协调节点) 接收用户请求、转发请求、汇总结果

生产最佳实践 :超过 5 节点的集群必须角色分离------候选主节点独立部署,不承担数据存储压力。

7.3 分片与副本

分片(Primary Shard) :

  • 文档写入的唯一入口
  • 索引创建时指定数量,创建后不可修改(这是面试高频考点!)
  • 如需修改,只能重建索引

副本(Replica Shard) :

  • 主分片的完整拷贝
  • 高可用保障:主分片故障时,副本可提升为新主分片
  • 读负载均衡:副本可承接读请求
  • 数量可动态修改
  • 必须与主分片分布在不同的节点上

7.4 高可用机制

  • Master 选举:基于 Raft 共识算法
  • 脑裂防护 :配置 minimum_master_nodes = (候选主节点数 / 2) + 1
  • 故障自愈:节点故障 → 副本升主 → 新副本补齐 → 分片重平衡
  • 快照备份:支持增量备份至 HDFS、对象存储
  • ILM 索引生命周期管理:热-温-冷-冻分层存储

八、读写流程原理

8.1 写入流程

复制代码
Client → Coordinating Node → 路由计算 → Primary Shard → 同步 Replica → 返回 ACK

详细步骤:

  1. 客户端向协调节点发送写请求
  2. 协调节点对 _id 进行哈希路由,计算文档属于哪个分片
  3. 将请求转发给对应的 Primary Shard
  4. 数据先写入 memory buffer ,再写入 translog(事务日志)
  5. 并行同步到所有 Replica Shard
  6. 所有副本确认后,返回成功给客户端

为什么分片数不能改? 因为路由算法 shard = hash(_id) % number_of_shards 依赖分片数量,改了会导致数据找不到。

8.2 读取流程

复制代码
Client → Coordinating Node → 路由计算 → Primary/Replica(轮询)→ 返回文档

详细步骤:

  1. 客户端向协调节点发送读请求
  2. 协调节点对 _id 进行哈希路由,确定分片位置
  3. 使用 round-robin 轮询算法 ,在 Primary 和所有 Replica 中随机选择一个
  4. 从选中的分片读取文档并返回

💡 读请求可以从 Primary 或 Replica 读取,实现读负载均衡。

九、生产环境性能优化(加分项)

9.1 分片规划黄金法则

  • 单个分片推荐大小:20GB - 50GB
  • 过大 → 故障恢复慢
  • 过小 → 分片数量过多,元数据管理压力大
  • 单个节点分片数 ≤ 磁盘容量(GB) × 20

9.2 索引优化

  • 显式定义 Mapping ,关闭动态映射(dynamic: false 或 strict)
  • 禁用不需要的字段 :"index": false、"doc_values": false
  • 合理设置刷新间隔 :index.refresh_interval(写入密集时可调大)
  • 使用索引模板统一管理 settings 和 mappings

9.3 查询优化

  • 使用 Filter 上下文(不参与评分,可缓存)
  • 避免深度分页 (from + size 过大),使用 search_after 或 Scroll
  • 控制聚合的 size 和 shard_size,避免高基数字段聚合
  • 合理使用 _source:只返回需要的字段

9.4 硬件与 JVM

  • filesystem cache 要留足够内存:ES 严重依赖 OS 缓存
  • JVM 堆内存不超过 32GB(压缩指针失效)
  • 堆内存留一半给 OS 缓存

十、面试高频题汇总

问题 核心考点
ES 为什么比数据库快? 倒排索引 + 内存缓存,避免全表扫描
text 和 keyword 区别? 分词 vs 不分词、聚合排序支持
主分片数量能改吗? 不能,创建后不可修改
副本数量能改吗? 能,动态修改
ES 是实时搜索吗? 准实时(NRT),refresh 后才可见
terms 聚合精确吗? 默认近似,可调大 shard_size
如何处理集群 Yellow/Red? Yellow:副本不足;Red:主分片丢失
如何避免脑裂? 配置 minimum_master_nodes
RestHighLevelClient 还能用吗? 7.15 弃用,8.0 移除,推荐 Java API Client

以上便是 Elasticsearch 从核心原理到生产实践的完整知识体系。掌握这些内容,足以应对绝大多数 ES 相关的面试与日常工作场景。

相关推荐
差不多的周周8 小时前
《骨架上下文:基于零镜头骨架的动作识别的骨架侧上下文提示学习》论文核心部分详解
学习
Century_Dragon9 小时前
从挂图到VR:汽修专业的一堂新能源原理课可以怎么上~
学习
frjc9 小时前
数据库选型:如何从众多数据库中选出最理想的那一个
redis·mysql·clickhouse·elasticsearch
知识分享小能手10 小时前
C++ 学习教程,从入门到精通,C++对象和类 — 详细知识点总结(10)
开发语言·c++·学习
New农民工10 小时前
倒立摆训练秘籍:理解训练参数
学习
无序的浪11 小时前
测试博客-基于微服务的在线判题系统
java·spring cloud·docker·微服务·测试·在线判题
L@ncor11 小时前
第五章 基于低代码平台的智能体搭建 · 学习笔记(Coze / Dify / FastGPT / n8n)
笔记·学习·低代码·agent·prompt工程
迪丽热爱11 小时前
多媒体应用20-903(补)
学习
天远Date Lab12 小时前
微服务架构实战:基于天远学历信息高级版构建自动化人才准入网关
人工智能·微服务·架构·自动化
传奇开心果编程13 小时前
【Flutter入门练中学】第8课:动画与过渡
android·学习·flutter·ui·ios