Elasticsearch课程

Elasticsearch 第一单元

一、ES 基础认知(理论)

ES 是分布式全文检索引擎,底层基于 Lucene,用 Java 开发,主打海量文本检索、日志分析、多维聚合,常用于 ELK/ELFK、商品搜索、日志平台。

Elasticsearch 关系型数据库 MySQL 说明
Index(索引) Database 数据库 存放一类数据,如 order_loggoods
Type(类型) Table 表 ES7.0 后彻底废弃,一个索引只能一种数据
Document(文档) Row 行数据 单条数据,JSON 格式,ES 最小存储单元
Field(字段) Column 列 文档内每个键值对,如 name、age、content
Mapping(映射) Schema 表结构 定义字段类型、分词、索引规则
Shard(分片) 分库分表 拆分大索引,实现水平扩容,分主分片 + 副本分片
Query DSL SQL ES 专用 JSON 查询语法

二、核心概念详解

1.集群基础操作

实操 1:查看集群健康状态
复制代码
GET _cluster/health

返回关键字段:

  • status:green/yellow/red
  • number_of_nodes:集群节点总数
  • active_shards:正常分片数

状态解释:

  1. green:主、副本分片全部正常,集群健康
  2. yellow:主分片全部正常,副本未分配(单机单节点必黄,数据不丢)
  3. red:存在主分片丢失,索引损坏、数据丢失
实操 2:查看集群所有节点信息
复制代码
GET _cat/nodes?v

v 代表 verbose,打印表头;可看到节点角色、内存、CPU、磁盘占用

实操 3:查看集群分片分配详情
复制代码
GET _cat/shards?v

2.Index 索引(库)

实操 1:创建索引(指定主分片、副本数量)

创建索引 goods,3 个主分片,1 个副本:

复制代码
PUT goods
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}

关键规则:

  • number_of_shards 主分片:索引创建后无法修改
  • number_of_replicas 副本:可随时动态调整
实操 2:查看单个索引配置
复制代码
GET goods/_settings
实操 3:修改副本数量(动态调整)
复制代码
PUT goods/_settings
{
  "number_of_replicas": 0
}
实操 4:查看集群全部索引
复制代码
GET _cat/indices?v
实操 5:删除索引(危险操作)
复制代码
DELETE goods

3 Document 文档(行数据,JSON)

内置系统字段:

  • _id:文档唯一标识,自定义 / 自动生成
  • _index:归属索引
  • _source:原始完整 JSON 数据
  • _score:全文检索匹配分值
实操 1:新增文档(指定自定义_id=1)
复制代码
PUT goods/_doc/1
{
  "name": "华为Mate60手机",
  "price": 5999,
  "tag": ["手机","数码"],
  "create_time": "2026-07-19"
}
实操 2:新增文档,ES 自动生成_id
复制代码
POST goods/_doc
{
  "name": "小米14",
  "price": 3999,
  "tag": ["手机","性价比"]
}
实操 3:根据 ID 查询单条文档
复制代码
GET goods/_doc/1
实操 4:全量修改文档(覆盖整条数据)
复制代码
PUT goods/_doc/1
{
  "name": "华为Mate60 Pro",
  "price": 6499,
  "tag": ["手机","旗舰"],
  "create_time": "2026-07-19"
}
实操 5:局部更新文档(只改部分字段)
复制代码
POST goods/_update/1
{
  "doc": {
    "price": 6299
  }
}
实操 6:删除文档
复制代码
DELETE goods/_doc/1

4 Mapping 映射(表结构,定义字段类型)

两大模式:

  1. 动态 mapping:写入数据 ES 自动推断字段类型,生产不推荐,容易类型错乱
  2. 静态 mapping:创建索引时手动定义,企业标准用法

常用字段类型

  1. text:可分词,用于全文检索(商品名称、日志内容)
  2. keyword:不分词,精确匹配、聚合、排序(标签、手机号)
  3. long/integer/double:数值
  4. date:日期
  5. boolean:布尔
  6. object:普通嵌套对象
  7. nested:对象数组专用
实操 1:创建索引同时自定义静态 Mapping
复制代码
PUT product
{
  "settings": {
    "number_of_shards": 2,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "brand": {
        "type": "keyword"
      },
      "price": {
        "type": "integer"
      },
      "publish_time": {
        "type": "date",
        "format": "yyyy-MM-dd"
      }
    }
  }
}
实操 2:查看索引 Mapping 结构
复制代码
GET product/_mapping
实操 3:给已有索引新增字段(不能修改原有字段类型)
复制代码
PUT product/_mapping
{
  "properties": {
    "stock": {
      "type": "integer"
    }
  }
}

三、底层核心原理(理论)

1 .正向索引(数据库存储)

文档 ID → 完整文本

查询方式:遍历全部文档逐行匹配,海量数据性能极差。

2 .倒排索引(ES 检索极速的核心)

结构:分词词条 → 文档 ID 列表

示例文档:

doc1:华为 Mate60 旗舰手机

doc2:小米性价比手机

分词结果:华为、Mate60、旗舰、手机、小米、性价比

倒排链表:

手机:doc1, doc2

华为:doc1

小米:doc2 查询关键词直接匹配词条,不用遍历全量数据,毫秒级返回。

3 Segment 段、Translog 事务日志

  1. Segment:磁盘最小存储单元,新写入数据生成独立小段,后台自动 merge 合并;大量小段会消耗 CPU、IO。
  2. Translog:写入时同步记录事务日志,服务器宕机后依靠 translog 恢复未落地磁盘的数据。
  3. refresh(默认 1s):缓冲区数据生成新 segment,数据可被检索;频繁刷新产生大量小段。
  4. flush:将 segment 持久化磁盘,清空 translog。

四、集群节点与分片架构(理论)

1 节点 5 种角色

  1. master 主节点:管理集群元数据(索引、分片分配),同一集群仅 1 个活跃 master
  2. data 数据节点:存储分片,执行读写、聚合计算,业务核心
  3. coordinating 协调节点:接收客户端请求,分发分片任务、合并结果,所有节点默认拥有
  4. ingest 节点:写入前预处理文档清洗数据
  5. 冷热分层节点:hot 热写入、warm 温只读、cold 冷归档

2 分片 Shard

  1. 主分片 primary:创建索引指定,不可修改;文档通过hash(_id) % 主分片数路由到对应分片;所有写入先写主分片。
  2. 副本分片 replica:主分片备份,不能和主分片同节点;提升高可用、分担读压力;数量可动态调整。

3 故障转移机制

节点宕机 → 该节点分片离线 → 副本自动升级为新主分片 → 集群自动创建新副本维持配置副本数量。

Elasticsearch 第二单元:分词器、DSL 基础查询、文档写入底层流程

一、分词器 Analyzer(全文检索核心)

1. 分词器三组件完整执行流程

一段文本处理分三步,顺序固定:

  1. Character Filter 字符过滤器 清洗原始文本:去除 html 标签、替换特殊符号、替换敏感词,可配置多个。 例:<p>手机!</p>手机!
  2. Tokenizer 分词器(切词核心) 按规则把文本切割成一个个词条 token。 标准分词、空格分词、IK 中文分词都属于 Tokenizer。
  3. Token Filter 词条过滤器 对切好的词条二次处理:小写转换、停用词删除、同义词扩展、长度过滤。

2. ES 内置标准分词器 standard(默认)

特点:英文友好,中文会单字拆分,不适合中文业务。 测试分词 API:_analyze 调试工具

复制代码
GET _analyze
{
  "analyzer": "standard",
  "text": "华为Mate60 旗舰智能手机"
}

输出效果:华、为、Mate60、旗、舰、智、能、手、机(中文单字拆分,无法完整匹配短语)

3. 中文必备:IK 分词器(第三方插件)

两种模式:

  1. ik_smart:粗粒度分词,短句合并,适合索引存储
  2. ik_max_word:细粒度全切分,拆分所有组合词,适合搜索查询

IK 分词实操测试

复制代码
GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "华为Mate60 旗舰智能手机"
}

输出词条:华为、mate60、旗舰、智能、手机、智能手机

复制代码
GET _analyze
{
  "analyzer": "ik_smart",
  "text": "华为Mate60 旗舰智能手机"
}

输出词条:华为、mate60、旗舰智能手机

4. 自定义分词器(业务实战)

创建索引时自定义 analyzer,组合字符过滤、分词器、过滤:

复制代码
PUT goods
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_ik_analyzer": {
          "tokenizer": "ik_max_word",
          "filter": ["lowercase"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "my_ik_analyzer"
      },
      "brand": {
        "type": "keyword"
      }
    }
  }
}

5. 字段分词规则区分

  • text 字段:存入时使用 mapping 指定分词器生成倒排索引,查询时同样分词匹配,用于模糊搜索;
  • keyword 字段:不分词,完整原文作为单个词条,只能精确匹配,用于筛选、分组、排序。

二、Query DSL 基础查询语法

前置:查询两大分类

  1. 全文检索(match 系列):只作用于 text 字段,查询关键词会先分词再匹配,有相关性_score 分值
  2. 精确检索(term/terms/range):作用于 keyword、数值、日期,不分词完全匹配,无分值,适合 filter 过滤

2.1 匹配所有文档 match_all

复制代码
GET goods/_search
{
  "query": {
    "match_all": {}
  }
}

分页控制 size(默认 10 条)

复制代码
GET goods/_search
{
  "query": {
    "match_all": {}
  },
  "size": 20
}

2.2 全文检索 match(最常用)

text 字段分词模糊匹配

复制代码
GET goods/_search
{
  "query": {
    "match": {
      "title": "华为手机"
    }
  }
}

逻辑:"华为手机" 分词为华为、手机,文档包含任意一个词条都会返回,匹配越多分数越高。

2.3 短语匹配 match_phrase(词条必须连续)

要求关键词分词后在原文连续出现,精准短语搜索

复制代码
GET goods/_search
{
  "query": {
    "match_phrase": {
      "title": "华为手机"
    }
  }
}

2.4 多字段检索 multi_match

同时在多个 text 字段搜索关键词

复制代码
GET goods/_search
{
  "query": {
    "multi_match": {
      "query": "华为",
      "fields": ["title","desc"]
    }
  }
}

2.5 精确查询 term /terms(keyword 专用)

term:单个值精确匹配

复制代码
GET goods/_search
{
  "query": {
    "term": {
      "brand": {
        "value": "华为"
      }
    }
  }
}

terms:多值匹配,等价 SQL in

复制代码
GET goods/_search
{
  "query": {
    "terms": {
      "brand": ["华为","小米"]
    }
  }
}

重要坑:不要用 term 查询 text 字段!text 存入是分词后的碎片,term 搜完整字符串匹配不到。

2.6 区间查询 range(价格、时间)

gte 大于等于,lte 小于等于,gt 大于,lt 小于

复制代码
GET goods/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 3000,
        "lte": 7000
      }
    }
  }
}

时间区间示例:

复制代码
GET goods/_search
{
  "query": {
    "range": {
      "create_time": {
        "gte": "2026-01-01",
        "lte": "2026-07-19"
      }
    }
  }
}

2.7 组合查询 bool(业务必用核心)

bool 包含 4 种子句:

  1. must:必须匹配,会计算相关性分值(AND)
  2. filter:必须匹配,不计算分值、自带缓存,性能更高,过滤条件优先放 filter
  3. should:可选匹配,满足越多分数越高(OR)
  4. must_not:一定不能匹配,无分值

示例:华为手机,价格 3000~8000,排除苹果

复制代码
GET goods/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {"title": "手机"}}
      ],
      "filter": [
        {"term": {"brand": "华为"}},
        {"range": {"price": {"gte":3000,"lte":8000}}}
      ],
      "must_not": [
        {"term": {"brand": "苹果"}}
      ]
    }
  }
}

2.8 排序 sort

keyword、数值、日期字段支持排序;text 字段不能直接排序

复制代码
GET goods/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {"price": "desc"},
    {"create_time": "asc"}
  ]
}

三、文档写入、更新、删除底层完整流程

3.1 写入完整流程

  1. 请求发送至协调节点,通过 hash(_id) % 主分片数 路由到对应主分片节点

  2. 主分片接收数据:

    • 写入内存缓冲区(内存段)
    • 同步写入 translog 事务日志(持久化磁盘,宕机恢复数据)
  3. 主分片同步数据到所有副本分片,全部副本写入成功后,返回客户端写入成功

  4. refresh(默认 1 秒) 缓冲区数据生成独立 Segment 小段,写入内存,数据变为可检索;频繁手动 refresh 会生成大量小段,加重合并压力。 手动刷新 API:

    复制代码
    POST goods/_refresh
  5. flush(后台定时执行) 将内存 Segment 持久化到磁盘,清空 translog,落地后就算断电数据不丢失。 手动刷盘:

    复制代码
    POST goods/_flush
  6. Segment Merge 段合并 后台线程自动将多个小 Segment 合并为大 Segment,同时清理标记删除的文档;合并消耗 CPU/IO,是集群常见性能瓶颈。

3.2 更新底层原理(无原地修改es故意设置)

ES 底层不支持修改已有 Segment 数据,更新逻辑:

  1. 根据_id 查询旧文档,给旧文档打上「删除标记」,查询时自动过滤标记删除数据
  2. 写入一条全新完整文档覆盖
  3. 旧文档不会立刻删除,仅段合并时永久清除 大量频繁更新会产生海量小段与删除标记,性能大幅下降。

3.3 删除底层原理

  1. 对应文档添加删除标记,不会立刻从磁盘清除
  2. 仅在 Segment 合并阶段,彻底移除带删除标记的数据释放磁盘空间

3.4 translog 作用

  • 缓冲阶段数据仅在内存,断电丢失风险,translog 实时落盘兜底;
  • flush 成功后 translog 清空,不需要重复恢复;
  • 集群重启时读取 translog 恢复未 flush 的数据。

Elasticsearch 第三单元:聚合 Aggs、分页问题、集群运维、经典三大问题、生产最佳实践

一、聚合 Aggs(多维统计,ES 核心优势)

聚合分为两大类:桶聚合 Bucket(分组) + 指标聚合 Metric(计算数值)

1. 指标聚合 Metric(单字段统计,无分组)

用于计算最大 / 最小 / 平均 / 总和、去重数量

  • avg:平均值
  • sum:求和
  • max/min:最大最小值
  • count:文档总数
  • cardinality:基数(去重计数,类似 distinct)

实操:商品价格统计

复制代码
GET product/_search
{
  "size": 0,  // 不需要返回原始文档,只看聚合结果
  "aggs": {
    "price_stats": { // 自定义聚合名称
      "stats": { // 一次性返回avg/sum/max/min/count
        "field": "price"
      }
    },
    "brand_distinct": {
      "cardinality": {
        "field": "brand"
      }
    }
  }
}

2. 桶聚合 Bucket(分组,类似 group by)

(1)terms 普通分组(按关键词字段分组)

统计每个品牌商品数量、均价

复制代码
GET product/_search
{
  "size": 0,
  "aggs": {
    "group_by_brand": {
      "terms": {
        "field": "brand",
        "size": 10 // 返回前10个分组
      },
      "aggs": { // 分组内嵌套指标聚合
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}
(2)date_histogram 时间直方图(日志最常用,按时间分组)

按天统计商品上架数量,interval 可选:hour/day/month/year

复制代码
GET product/_search
{
  "size": 0,
  "aggs": {
    "group_by_day": {
      "date_histogram": {
        "field": "online_time",
        "calendar_interval": "day",
        "format": "yyyy-MM-dd"
      }
    }
  }
}
(3)range 区间分组

按价格区间分组:0-3000、3000-6000、6000 以上

复制代码
GET product/_search
{
  "size": 0,
  "aggs": {
    "price_range_group": {
      "range": {
        "field": "price",
        "ranges": [
          {"to": 3000},
          {"from": 3000, "to": 6000},
          {"from": 6000}
        ]
      }
    }
  }
}

3. 聚合过滤搭配查询

先筛选华为商品,再做聚合统计

复制代码
GET product/_search
{
  "size": 0,
  "query": {
    "term": {
      "brand": "华为"
    }
  },
  "aggs": {
    "huawei_price_avg": {
      "avg": {"field": "price"}
    }
  }
}

二、分页方案与深度分页性能灾难

1. from + size(前端普通分页,有上限缺陷)

语法示例:查询第 100 页,每页 10 条

复制代码
GET product/_search
{
  "from": 990,
  "size": 10,
  "query": {
    "match_all": {}
  }
}

致命问题

ES 需要将所有分片前 10000 条数据全部加载到内存排序,页数越大内存消耗越高,默认 index.max_result_window:10000,超过直接报错。

适用场景:只允许前几十页,不支持无限下拉翻页

2. scroll 游标分页(海量数据导出,不适合前端翻页)

原理:生成快照,保存排序上下文,持续滚动拉取全量数据

实操

  1. 初始化 scroll,有效期 5 分钟

    GET product/_search?scroll=5m
    {
    "size": 100,
    "query": {"match_all": {}}
    }

返回结果中携带 _scroll_id 2. 使用 scroll_id 循环拉取下一批

复制代码
GET _search/scroll
{
  "scroll": "5m",
  "scroll_id": "返回的scroll_id值"
}

缺点

无法实时获取新增数据,占用集群内存,不适合前端分页,仅用于离线导出数据

3. search_after(推荐!大数据下拉分页,无深度限制)(类似于游标,惰性求值)

基于上一页最后一条文档的排序值向后滚动,无 10000 条上限

使用前提:必须指定唯一稳定排序字段(如 _id)

复制代码
// 第一页
GET product/_search
{
  "size": 10,
  "sort": [
    {"price": "desc"},
    {"_id": "asc"} // 唯一兜底排序
  ]
}

拿到最后一条文档 sort 数组 [5999, "1"]

复制代码
// 下一页
GET product/_search
{
  "size": 10,
  "search_after": [5999, "1"],
  "sort": [
    {"price": "desc"},
    {"_id": "asc"}
  ]
}

优点:性能稳定,无深度分页限制,前端下拉加载首选

三、线上三大经典问题(缓存穿透、击穿、雪崩)

ES 常配合 Redis 缓存使用,三类问题通用

1. 缓存击穿

现象:某热点数据缓存过期,大量并发请求同时直达 ES,压垮集群 解决方案:

  1. 热点 key 设置永不过期
  2. 分布式互斥锁,同一时间只放行一个请求查询 ES

2. 缓存雪崩

现象:大量缓存 key 同一时刻过期,流量全部打入 ES 解决方案:

  1. 过期时间增加随机偏移值,打散过期时间
  2. 分层设置过期时间,避免集中失效

3. 缓存穿透

现象:查询不存在的数据,缓存无记录,每次都查询 ES(如非法 id) 解决方案:

  1. 布隆过滤器拦截不存在 id
  2. 查询空结果时,缓存空值,设置短过期时间

四、集群运维与生产实操命令

1. ILM 索引生命周期管理(ELK 日志必备)

自动完成:冷热迁移、收缩分片、删除过期日志 四阶段:

  • hot:热阶段,写入查询
  • warm:温阶段,只读,压缩分片
  • cold:冷阶段,归档,降低硬件成本
  • delete:删除过期索引

2. 快照 snapshot 索引备份恢复

  1. 创建快照仓库(存储备份文件路径)

  2. 对索引打快照全量备份

  3. 集群故障时从快照恢复索引数据 实操命令(创建快照)

    PUT /_snapshot/backup_repo/snapshot_20260719
    {
    "indices": "product",
    "ignore_unavailable": true,
    "include_global_state": false
    }

3. 冷热数据分层节点

节点配置标记 node.attr.data_type: hot/warm/cold,配合 ILM 自动迁移分片,节省服务器成本。

4. 分片生产最佳规范

  1. 单个分片数据控制在 10G ~ 30G
  2. 主分片数 = 数据节点数量倍数,均衡分片分配
  3. 生产副本数至少 1,单机测试副本 0
  4. 分片过多:大量 segment,merge 消耗 CPU;分片过少:单分片数据过大查询慢

5. 批量写入 _bulk 高性能 API

单请求批量增删改,大幅提升写入吞吐量 语法格式:操作元数据行 + 数据行,换行分隔

复制代码
POST product/_bulk
{"index":{"_id":4}}
{"title":"OPPO手机","brand":"OPPO","price":3499,"online_time":"2026-04-01"}
{"update":{"_id":3}}
{"doc":{"price":2299}}
{"delete":{"_id":2}}

规范:单批数据控制 500~1000 条,单批大小不超过 15MB

五、进阶高频知识点(面试必考)

1. BM25 打分算法

ES5.0 之后默认,替代老旧 TF-IDF 算法;解决 TF-IDF 长文档分值虚高问题,更贴合真实搜索场景,控制词条重复堆砌权重。

2. nested 嵌套对象

普通 object 数组会扁平化,导致关联查询错乱;nested 类型将数组对象独立存储,支持对象内部精准过滤、聚合。 创建 mapping 示例:

复制代码
PUT shop
{
  "mappings": {
    "properties": {
      "shop_name": {"type":"keyword"},
      "goods_list": {
        "type": "nested",
        "properties": {
          "goods_name": {"type":"text"},
          "price": {"type":"integer"}
        }
      }
    }
  }
}

查询时必须使用 nested 查询语法。

3. 路由 routing 自定义分片分配

文档默认用 _id 哈希分片;自定义 routing 字段可将关联数据存入同一分片,跨分片聚合性能大幅提升。 写入指定 routing:

复制代码
PUT product/_doc/1?routing=brand_huawei
{
  "title":"华为手机","brand":"华为"
}

查询时必须携带相同 routing,否则查不到数据。

相关推荐
码农小白AI17 小时前
工业设备验收迈入智能审核新时代:AI报告审核通审Agent版 IACheck打造检测报告质量管控新引擎
大数据·人工智能
智圣新创0118 小时前
高校共生型学生社区生态圈搭建 核心路径与效能升级高频实操答疑
大数据·人工智能
Achou.Wang19 小时前
《从零实现cobra》手写一个 mini 版 Cobra
大数据·elasticsearch·搜索引擎
BGK11235819 小时前
基于qemu_v8+optee 4.00 平台构建 ca/ta
java·大数据·数据库
OneNobody19 小时前
Spark SQL AQE工作原理源码剖析
大数据·sql·spark
工业HMI实战笔记20 小时前
【无标题】
大数据·人工智能·ui·自动化·人机交互·交互
Geoffwo20 小时前
脑机交互全流程原理解析
大数据·人工智能
大模型码小白21 小时前
向量化引擎与 AI 排障:当 SIMD 遇到异常检测,存储诊断的范式转移
java·大数据·数据库·人工智能·python
雪碧聊技术1 天前
中国可重复使用火箭首次成功着陆——航天“降本时代”正式开启
大数据·人工智能