目录
正在加载目录…

ES:核心概念、架构与使用场景

发布于 2026-05-26 10:25 · 最后编辑于 2026-07-31 15:52 · 字数 5,002 👁 87 次阅读

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 的集合,无完全对等概念
TableIndex(索引)Type 废弃后,Index 成为文档的直接容器,一个业务对象建一个 Index
RowDocument(文档)一条数据,JSON 格式
ColumnField(字段)文档中的一个属性
SchemaMapping字段类型定义
索引(B+ 树)Inverted Index(倒排索引)两者名字相同但概念不同,ES 的 Index 指倒排索引结构
SQLQuery 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 时构建。<br>⚠️ 不设置 index=true 时该字段无法搜索(MySQL 不设索引仍可查,ES 不设则返回空)B+ 树索引
召回对查询词分词后,通过倒排索引定位到文档的过程查询过程
召回量召回得到的文档数,即 hits.total.value查询返回结果数
分片(Shard)索引的子集,每个分片具备完整的索引结构无对应概念
段(Segment)分片的组成单元,检索的基本单元,所有查询/更新基于段执行
段合并(Merge)Lucene 删除是标记删除,更新是先删后增,段积累后需合并以清除无效数据、提升查询性能

倒排索引(核心原理)

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

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

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

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

  • 默认(OR):取并集 → 文档1、文档2、文档3 均返回(match 默认行为)
  • AND 语义:取交集 → 只返回文档1(需指定 "operator": "and"

无论 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网络数据包
WinlogbeatWindows 事件日志
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 写入一个文档时,会根据字段类型同时构建多套数据结构,各司其职:

数据结构作用存储位置
倒排索引词项 → 文档列表,用于搜索(matchtermLucene segment 文件
BKD 树数值/日期/地理范围查询(rangegeo_distanceLucene 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 树
booleantrue/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" }
  }
}
  • name:分词,支持全文搜索
  • name.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.relationeq: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部分主分片未分配,数据不完整,影响读写

分片策略建议

场景建议
单索引数据量 < 10GB1 主分片,1 副本
单索引数据量 10~100GB3~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}
查看 MappingGET/{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)

暂无评论,来留下第一条吧。
登录注册 后才能发表评论