ES 系列 #01:ES 简介

发布于 2026-05-26 10:25 👁 10 次阅读
#elasticsearch#lucene#search

Elasticsearch(简称 ES)是基于 Apache Lucene 构建的分布式全文搜索与分析引擎,由 Elastic 公司于 2010 年发布。 以 近实时(NRT)搜索、水平扩展、RESTful API 为核心特性,广泛用于日志分析(ELK Stack)、全文检索、商品搜索等场景。

⚠️ 版本说明:本文以 Elasticsearch 8.x 为基准。7.x 与 8.x 在安全配置、API 上有差异,差异处会注明。


目录

章节 说明
核心概念 索引、文档、分片、倒排索引、搜索术语、Elastic Stack 生态
安装与启动 本地安装、Docker 启动、Kibana
索引管理 创建索引、Mapping、Settings、别名
字段类型底层数据结构 各字段类型对应的 Lucene 存储实现
文档操作 增删改查(CRUD)、搜索响应字段解析
搜索查询 Query DSL、全文搜索、精确查询、复合查询
聚合分析 Metric、Bucket、Pipeline 聚合
分词器 内置分词器、中文分词(IK)、自定义分词器
集群与分片 节点角色、分片策略、副本、集群健康
快速参考卡 Query 类型速查、常用 API 速查

核心概念

与关系型数据库对比

ES 7.x 废弃、8.x 彻底移除了 Type 概念,下表以 8.x 现代对应关系为准。

关系型数据库 Elasticsearch 8.x 说明
数据库实例 ES 集群 多个 Index 的集合,无完全对等概念
Table Index(索引) Type 废弃后,Index 成为文档的直接容器,一个业务对象建一个 Index
Row Document(文档) 一条数据,JSON 格式
Column Field(字段) 文档中的一个属性
Schema Mapping 字段类型定义
索引(B+ 树) Inverted Index(倒排索引) 两者名字相同但概念不同,ES 的 Index 指倒排索引结构
SQL Query DSL 查询语言

Type 的历史:旧版 ES 中一个 Index 可含多个 Type(类似一个库多张表),7.x 废弃、8.x 移除。移除后 Index 直接承担 Table 的角色,现代实践中"一个业务对象 = 一个 Index"。

搜索专业术语

术语 说明 对应数据库概念
字段(Field) 文档的组成单元,包含字段名称、属性和内容
字段属性(Attributes) 描述字段的元数据,包括类型、是否建索引、是否分词等 VARCHAR(16) / index / primary_key
文档(Document) 可搜索的结构化数据单元,JSON 格式,由多个字段组成 行记录
索引(Index) 多个文档的集合
正排 文档 → 字段值的映射链表(doc1 → id, type, time…)。分两层:① _source(原始 JSON,默认存储,用于返回结果)② doc_values(列式存储,用于排序/聚合)。对所有支持的字段类型默认开启,仅 text/annotated_text 不支持(需用 keyword 子字段替代) 行记录
倒排 词 → 文档列表的映射链表(term1 → doc1, doc2, doc3),字段设置 index=true 时构建。
⚠️ 不设置 index=true 时该字段无法搜索(MySQL 不设索引仍可查,ES 不设则返回空)
B+ 树索引
召回 对查询词分词后,通过倒排索引定位到文档的过程 查询过程
召回量 召回得到的文档数,即 hits.total.value 查询返回结果数
分片(Shard) 索引的子集,每个分片具备完整的索引结构 无对应概念
段(Segment) 分片的组成单元,检索的基本单元,所有查询/更新基于段执行
段合并(Merge) Lucene 删除是标记删除,更新是先删后增,段积累后需合并以清除无效数据、提升查询性能

倒排索引(核心原理)

正排索引(传统):文档 → 词
倒排索引(ES)  :词   → 文档列表

示例:
  文档1:"苹果手机价格"
  文档2:"苹果电脑优惠"
  文档3:"手机价格对比"

倒排索引:
  苹果 → [文档1, 文档2]
  手机 → [文档1, 文档3]
  价格 → [文档1, 文档3]
  电脑 → [文档2]

搜索"苹果手机"时,先分词得到 ["苹果", "手机"],再查倒排索引:

无论 OR 还是 AND,都只需查词表后做集合运算,速度极快,无需全文扫描。

Elastic Stack 生态

ES 的能力通过 Elastic Stack(ELK Stack)发挥最大价值,四大组件各司其职:

graph LR
    A["数据源<br/>(日志/指标/事件)"] --> B["Beats<br/>轻量采集器"]
    A --> C["Logstash<br/>数据处理管道"]
    B --> C
    B --> D["Elasticsearch<br/>存储 & 搜索 & 分析"]
    C --> D
    D --> E["Kibana<br/>可视化 & 管理"]
组件 定位 说明
Elasticsearch 核心引擎 分布式搜索与分析,所有数据的存储和检索中心
Kibana 可视化层 柱状图、折线图、饼图等可视化,实时呈现 ES 聚合数据
Logstash 数据处理管道 从多来源采集数据,支持丰富过滤器将非结构化数据转为结构化数据后写入 ES
Beats 轻量采集器 轻量级单一用途采集器,直接将数据发送给 ES 或 Logstash

Beats 采集器列表:

Beats 采集内容
Filebeat 日志文件
Metricbeat 系统/服务指标
Packetbeat 网络数据包
Winlogbeat Windows 事件日志
Auditbeat 审计数据
Heartbeat 服务运行时间监控
Functionbeat 无服务器(Serverless)函数采集

Beats vs Logstash:Beats 轻量简单,适合大量节点部署;Logstash 功能更强,支持复杂的数据转换和过滤,两者常配合使用。

分片(Shard)

概念 说明
主分片(Primary Shard) 数据的实际存储单元,创建索引时指定,不可更改
副本分片(Replica Shard) 主分片的拷贝,提供高可用和读扩展,可动态调整
分片数建议 单分片不超过 50GB,节点数 × 1~3 倍为宜

安装与启动

Docker 快速启动(推荐开发环境)

# 启动单节点 ES(8.x 默认开启安全认证)
docker run -d \
  --name elasticsearch \
  -p 9200:9200 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
  elasticsearch:8.13.0

# 验证
curl http://localhost:9200
# 返回集群信息 JSON 即成功

# 同时启动 Kibana(可视化管理界面)
docker run -d \
  --name kibana \
  -p 5601:5601 \
  -e "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" \
  --link elasticsearch \
  kibana:8.13.0
# 访问 http://localhost:5601

macOS 本地安装

brew tap elastic/tap
brew install elastic/tap/elasticsearch-full

# 启动
brew services start elastic/tap/elasticsearch-full

# 验证
curl http://localhost:9200

索引管理

创建索引(含 Mapping 和 Settings)

# 创建商品索引
curl -X PUT "localhost:9200/products" -H 'Content-Type: application/json' -d '
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "ik_max_word",
          "filter": ["lowercase"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "id":          { "type": "long" },
      "name":        { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" },
      "description": { "type": "text", "analyzer": "ik_max_word" },
      "price":       { "type": "scaled_float", "scaling_factor": 100 },
      "category":    { "type": "keyword" },
      "tags":        { "type": "keyword" },
      "status":      { "type": "byte" },
      "stock":       { "type": "integer" },
      "created_at":  { "type": "date", "format": "yyyy-MM-dd HH:mm:ss||epoch_millis" },
      "location":    { "type": "geo_point" }
    }
  }
}'

常用字段类型

类型 说明 适用场景
text 分词后索引,支持全文搜索 标题、描述、正文
keyword 不分词,精确匹配、排序、聚合 状态、分类、标签、ID
long / integer 整数 数量、ID
scaled_float 缩放浮点(推荐替代 float) 价格、评分
date 日期时间 时间戳、日期
boolean 布尔值 开关状态
geo_point 地理坐标 LBS 位置
nested 嵌套对象(保持对象间关系) 订单明细、评论列表
object 普通对象(扁平化存储) 简单嵌套结构

字段类型底层数据结构

ES 写入一个文档时,会根据字段类型同时构建多套数据结构,各司其职:

数据结构 作用 存储位置
倒排索引 词项 → 文档列表,用于搜索(matchterm Lucene segment 文件
BKD 树 数值/日期/地理范围查询(rangegeo_distance Lucene segment 文件
Doc Values 列存,用于排序(sort)、聚合(aggs)、脚本 磁盘列文件,OS 缓存
_source 原始 JSON,用于返回文档内容 独立压缩存储

字段类型 → 底层结构对照

字段类型 倒排索引 BKD 树 Doc Values 说明
text ✅(分词后) 只支持全文搜索,不能排序/聚合(需加 keyword 子字段)
keyword ✅(整体作 term) 精确匹配 + 排序 + 聚合
integer / long 范围查询走 BKD 树,排序/聚合走 Doc Values
float / double 同上
date ✅(date 转 long) 日期范围查询走 BKD 树
boolean true/false 精确匹配
geo_point ✅(二维 BKD) 地理范围/距离查询
nested 视子字段 视子字段 每个 nested 对象独立建索引

一个字段同时拥有多套结构

字段 price: 8999.00(scaled_float)

写入时同时构建:
  ├─ BKD 树   → 支持 price >= 1000 AND price <= 10000 的范围查询
  ├─ Doc Values → 支持 ORDER BY price、AVG(price) 聚合
  └─ _source  → 支持返回原始值
字段 category: "手机"(keyword)

写入时同时构建:
  ├─ 倒排索引  → 支持 term: { category: "手机" } 精确匹配
  ├─ Doc Values → 支持 terms 聚合(按分类统计数量)
  └─ _source  → 支持返回原始值

text 字段为什么不能直接排序/聚合

字段 name: "苹果手机"(text,分词后)

倒排索引中存的是:
  苹果 → [doc1, doc2]
  手机 → [doc1, doc3]

没有 Doc Values(分词后无法列存),无法知道 doc1 的 name 原始值是什么
→ 排序/聚合需要完整字段值,text 做不到

解决方案:定义 fields 子字段,同一数据建两套索引:

"name": {
  "type": "text",
  "analyzer": "ik_max_word",
  "fields": {
    "keyword": { "type": "keyword" }
  }
}

BKD 树 vs 倒排索引的选择

ES 5.0 之前数值字段也用倒排索引(把数字转为特殊 term),范围查询需扫描大量 term。5.0 后换成 BKD 树:

场景 倒排索引 BKD 树
term: { status: 1 } 精确匹配 ✅ 快
range: { price: { gte: 100 } } 范围查询 慢(扫描大量 term) ✅ 快(O(log N))
地理距离查询 无法支持 ✅ 支持(多维 BKD)

索引管理操作

# 查看索引信息
curl "localhost:9200/products"

# 查看 Mapping
curl "localhost:9200/products/_mapping"

# 查看 Settings
curl "localhost:9200/products/_settings"

# 新增字段(Mapping 只能新增,不能修改已有字段类型)
curl -X PUT "localhost:9200/products/_mapping" -H 'Content-Type: application/json' -d '
{
  "properties": {
    "brand": { "type": "keyword" }
  }
}'

# 删除索引
curl -X DELETE "localhost:9200/products"

# 索引别名(零停机切换索引的关键)
curl -X POST "localhost:9200/_aliases" -H 'Content-Type: application/json' -d '
{
  "actions": [
    { "add": { "index": "products_v2", "alias": "products" } },
    { "remove": { "index": "products_v1", "alias": "products" } }
  ]
}'

文档操作

es write flow

增删改查

# 新增文档(指定 ID)
curl -X PUT "localhost:9200/products/_doc/1" -H 'Content-Type: application/json' -d '
{
  "id": 1,
  "name": "iPhone 15 Pro",
  "description": "苹果旗舰手机,A17 Pro 芯片",
  "price": 8999.00,
  "category": "手机",
  "tags": ["苹果", "旗舰", "5G"],
  "status": 1,
  "stock": 100,
  "created_at": "2024-01-01 00:00:00"
}'

# 新增文档(自动生成 ID)
curl -X POST "localhost:9200/products/_doc" -H 'Content-Type: application/json' -d '
{ "name": "MacBook Pro", "price": 14999.00, "category": "电脑" }'

# 查询文档
curl "localhost:9200/products/_doc/1"

# 全量更新(替换整个文档)
curl -X PUT "localhost:9200/products/_doc/1" -H 'Content-Type: application/json' -d '
{ "name": "iPhone 15 Pro Max", "price": 9999.00, "category": "手机" }'

# 部分更新(只更新指定字段)
curl -X POST "localhost:9200/products/_update/1" -H 'Content-Type: application/json' -d '
{
  "doc": {
    "price": 8499.00,
    "stock": 80
  }
}'

# 脚本更新(动态修改)
curl -X POST "localhost:9200/products/_update/1" -H 'Content-Type: application/json' -d '
{
  "script": {
    "source": "ctx._source.stock -= params.count",
    "params": { "count": 10 }
  }
}'

# 删除文档
curl -X DELETE "localhost:9200/products/_doc/1"

# 批量操作(bulk API,生产环境推荐,减少网络开销)
curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' -d '
{ "index": { "_index": "products", "_id": "2" } }
{ "name": "iPad Pro", "price": 6999.00, "category": "平板" }
{ "index": { "_index": "products", "_id": "3" } }
{ "name": "AirPods Pro", "price": 1899.00, "category": "耳机" }
{ "delete": { "_index": "products", "_id": "99" } }
'

搜索响应字段解析

ES _search 返回的响应体各字段含义:

字段 含义 备注
took 查询耗时(ms) 第一次查询较慢,数据需从磁盘 load
timed_out 是否超时(boolean) 未指定 timeout 参数时默认 false
_shards.total 应遍历的分片数
_shards.successful 成功返回数据的分片数
_shards.failed 查询失败的分片数
_shards.skipped 跳过的分片数 超时时可能跳过部分分片
hits.total.value 命中文档数 ES 5.x 后不保证精确,仅为估算
hits.total.relation eq:value 即准确总数;gte:实际总数 ≥ value 大数据量时为 gte
hits.max_score 最高相关性得分 null=不追踪;0=不计算相关分
hits.hits[]._score 该文档与 query 的相关性得分 分数越高相关性越强
hits.hits[]._source 文档原始内容 可通过 _source 字段指定返回哪些字段

搜索查询

全文搜索(match)

# match:对搜索词分词后查询(最常用)
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "match": {
      "name": "苹果手机"
    }
  }
}'

# match_phrase:短语匹配,词序一致且相邻
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "match_phrase": {
      "description": "旗舰手机"
    }
  }
}'

# multi_match:多字段搜索
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "multi_match": {
      "query": "苹果手机",
      "fields": ["name^3", "description"],
      "type": "best_fields"
    }
  }
}'

精确查询

# term:精确匹配 keyword 字段(不分词)
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "term": { "category": "手机" }
  }
}'

# terms:多值精确匹配(类似 SQL IN)
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "terms": { "category": ["手机", "平板"] }
  }
}'

# range:范围查询
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "range": {
      "price": { "gte": 1000, "lte": 5000 }
    }
  }
}'

# exists:字段存在查询
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "exists": { "field": "brand" }
  }
}'

复合查询(bool)

# bool 查询:must(AND)、should(OR)、must_not(NOT)、filter(不计分)
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "苹果" } }
      ],
      "filter": [
        { "term":  { "status": 1 } },
        { "range": { "price": { "lte": 10000 } } }
      ],
      "must_not": [
        { "term": { "category": "配件" } }
      ],
      "should": [
        { "term": { "tags": "旗舰" } }
      ],
      "minimum_should_match": 0
    }
  },
  "sort": [
    { "price": { "order": "asc" } },
    "_score"
  ],
  "from": 0,
  "size": 10,
  "_source": ["id", "name", "price", "category"]
}'

must vs filtermust 参与相关性评分(_score),filter 不计分但会被缓存,纯过滤条件优先用 filter


聚合分析

# 组合查询 + 聚合:各分类商品数量及平均价格
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '
{
  "size": 0,
  "query": {
    "term": { "status": 1 }
  },
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category",
        "size": 10,
        "order": { "_count": "desc" }
      },
      "aggs": {
        "avg_price": {
          "avg": { "field": "price" }
        },
        "max_price": {
          "max": { "field": "price" }
        },
        "price_ranges": {
          "range": {
            "field": "price",
            "ranges": [
              { "to": 1000 },
              { "from": 1000, "to": 5000 },
              { "from": 5000 }
            ]
          }
        }
      }
    },
    "total_value": {
      "sum": { "field": "price" }
    }
  }
}'

聚合类型速查

类型 说明 示例
terms 按字段值分组(类似 GROUP BY) 按分类统计数量
date_histogram 按时间区间分组 按天/月统计订单
range 按数值区间分组 价格区间分布
avg / sum / min / max 数值统计 平均价格
cardinality 去重计数(近似) 独立用户数
top_hits 每组取 top N 文档 每分类取最贵商品
nested 嵌套对象聚合 订单明细统计

分词器

内置分词器

分词器 说明 示例
standard 默认,按 Unicode 分词,小写化 "Hello World"[hello, world]
whitespace 按空白分词,不小写化 "Hello World"[Hello, World]
keyword 不分词,整体作为一个 token "iPhone 15"[iPhone 15]
simple 按非字母分词,小写化 "hello-world"[hello, world]
english 英文词干提取(stemming) "running"[run]

中文分词(IK 插件)

# 安装 IK 分词器(版本需与 ES 一致)
# Docker 方式:--batch 自动跳过权限确认交互,避免 TTY 报错
docker exec elasticsearch \
  bin/elasticsearch-plugin install --batch \
  https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-8.13.0.zip

# 安装后重启容器生效
# docker restart elasticsearch

# ik_max_word:最细粒度分词(索引时用)
# "苹果手机价格" → [苹果手机, 苹果, 手机, 价格]

# ik_smart:智能分词(搜索时用,减少无意义匹配)
# "苹果手机价格" → [苹果, 手机, 价格]
# 测试分词效果
curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d '
{
  "analyzer": "ik_max_word",
  "text": "苹果手机价格对比"
}'

集群与分片

es arch

集群健康状态

# 查看集群健康
curl "localhost:9200/_cluster/health?pretty"

# 查看节点信息
curl "localhost:9200/_cat/nodes?v"

# 查看索引分片分布
curl "localhost:9200/_cat/shards?v"

# 查看索引列表(含文档数、存储大小)
curl "localhost:9200/_cat/indices?v&s=index"
颜色 含义
🟢 Green 所有主分片和副本分片均正常
🟡 Yellow 所有主分片正常,部分副本分片未分配(单节点时常见)
🔴 Red 部分主分片未分配,数据不完整,影响读写

分片策略建议

场景 建议
单索引数据量 < 10GB 1 主分片,1 副本
单索引数据量 10~100GB 3~5 主分片,1 副本
日志类(按天/月建索引) 每个索引 1~2 主分片,使用 ILM 自动管理
分片大小 建议 20~50GB,不超过 50GB

快速参考卡

Query 类型速查

Query 类型 说明
match 全文 分词后查询,最常用
match_phrase 全文 短语匹配,词序一致
multi_match 全文 多字段全文搜索
term 精确 keyword 字段精确匹配
terms 精确 多值匹配(IN)
range 精确 范围查询(gt/gte/lt/lte)
exists 精确 字段是否存在
wildcard 精确 通配符(*/?),性能差慎用
bool 复合 must/should/must_not/filter 组合
ids 精确 按文档 ID 查询

常用 API 速查

操作 方法 URL
集群健康 GET /_cluster/health
索引列表 GET /_cat/indices?v
创建索引 PUT /{index}
删除索引 DELETE /{index}
查看 Mapping GET /{index}/_mapping
新增文档 POST /{index}/_doc
指定 ID 写入 PUT /{index}/_doc/{id}
查询文档 GET /{index}/_doc/{id}
部分更新 POST /{index}/_update/{id}
删除文档 DELETE /{index}/_doc/{id}
搜索 GET/POST /{index}/_search
批量操作 POST /_bulk
分析分词 POST /_analyze
刷新索引 POST /{index}/_refresh

参考资料

← 返回列表

评论 (0)

暂无评论,来留下第一条吧。

发表评论