Elasticsearch 基础
核心概念
与传统数据库对比
| Elasticsearch | RDBMS | 说明 |
|---|---|---|
| Index(索引) | Database | 逻辑命名空间 |
| Type(类型,7.x 废弃) | Table | 8.x 中完全移除 |
| Document(文档) | Row | 数据基本单位,JSON 格式 |
| Field(字段) | Column | 字段映射 |
| Mapping(映射) | Schema | 字段类型定义 |
| Shard(分片) | 分表 | 数据水平拆分 |
| Replica(副本) | 主从复制 | 高可用 |
倒排索引(Inverted Index)
Elasticsearch 的核心是倒排索引,与传统 B+ 树索引相反:
text
文档集合:
Doc1: "Elasticsearch 是一个搜索引擎"
Doc2: "搜索引擎使用倒排索引"
Doc3: "Elasticsearch 基于 Lucene"
倒排索引:
┌─────────────┬─────────────────────────┐
│ 词条 │ 文档列表 (Posting List)│
├─────────────┼─────────────────────────┤
│ elasticsearch │ [Doc1, Doc3] │
│ 搜索引擎 │ [Doc1, Doc2] │
│ 倒排索引 │ [Doc2] │
│ lucene │ [Doc3] │
└─────────────┴─────────────────────────┘
查找 "Elasticsearch 引擎":
elasticsearch → [Doc1, Doc3]
引擎 → 没有,近似 → 搜索引擎 → [Doc1, Doc2]
交集 → Doc1优势:
- O(1) 查找单个词条
- 支持 TF-IDF / BM25 相关性评分
- 支持前缀、模糊、同义词搜索
索引与映射
创建索引
json
PUT /articles
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"analysis": {
"analyzer": {
"ik_smart_analyzer": {
"type": "custom",
"tokenizer": "ik_smart"
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": { "type": "keyword" }
}
},
"content": { "type": "text" },
"author": {
"type": "keyword"
},
"publishDate": { "type": "date" },
"views": { "type": "integer" },
"tags": { "type": "keyword" },
"isPublished": { "type": "boolean" },
"location": { "type": "geo_point" }
}
}
}字段类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
| text | 全文检索,分词后索引 | 标题、正文 |
| keyword | 精确匹配,不分词 | 标签、状态、ID |
| integer / long | 整数 | 计数、年龄 |
| float / double | 浮点数 | 价格、坐标 |
| boolean | 布尔值 | 开关、状态 |
| date | 日期,支持多种格式 | 时间戳 |
| geo_point | 经纬度点 | 位置搜索 |
| nested | 嵌套对象(独立查询) | 订单项、评论 |
动态映射
ES 会自动推断字段类型:
json
PUT /test-index/_doc/1
{ "name": "Alice", "age": 25, "registered": true }
// 自动推断:
// name → text(含 keyword 子字段)
// age → long
// registered → boolean可通过 dynamic: strict 禁止自动添加字段。
文档 CRUD
写入
json
// 明确指定 ID
PUT /articles/_doc/1
{
"title": "Elasticsearch 入门指南",
"content": "Elasticsearch 是一个分布式搜索引擎...",
"author": "Alice",
"publishDate": "2026-07-17",
"tags": ["elasticsearch", "搜索引擎"],
"views": 0,
"isPublished": true
}
// 自动生成 ID
POST /articles/_doc
{ "title": "新文章" }查询
json
// 根据 ID 查询
GET /articles/_doc/1
// 判断文档是否存在
HEAD /articles/_doc/1
// 批量查询
GET /articles/_mget
{ "ids": ["1", "2", "3"] }更新
json
// 局部更新
POST /articles/_update/1
{
"doc": { "views": 1 }
}
// 脚本更新(原子操作)
POST /articles/_update/1
{
"script": { "source": "ctx._source.views++" }
}删除
json
// 删除单文档
DELETE /articles/_doc/1
// 按查询删除
POST /articles/_delete_by_query
{
"query": { "match": { "isPublished": false } }
}搜索 DSL
Query Context vs Filter Context
| 上下文 | 作用 | 是否算分 | 能否缓存 |
|---|---|---|---|
| query | 相关性搜索(全文检索) | ✅ | ❌ |
| filter | 精确筛选(状态、日期范围) | ❌ | ✅ |
全文搜索
json
// match 查询(分词匹配)
GET /articles/_search
{
"query": {
"match": {
"title": "搜索引擎"
}
}
}
// multi_match(多字段搜索)
GET /articles/_search
{
"query": {
"multi_match": {
"query": "搜索引擎",
"fields": ["title^2", "content"]
// title 权重加倍
}
}
}
// match_phrase(短语精确匹配)
GET /articles/_search
{
"query": {
"match_phrase": {
"content": "倒排索引"
}
}
}精确查询(Filter)
json
GET /articles/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "author": "Alice" } },
{ "terms": { "tags": ["elasticsearch", "数据库"] } },
{ "range": { "views": { "gte": 100, "lte": 1000 } } },
{ "range": { "publishDate": { "gte": "2026-01-01" } } },
{ "exists": { "field": "content" } }
]
}
}
}复合查询(bool)
json
GET /articles/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "搜索引擎" } }
],
"must_not": [
{ "term": { "author": "Bob" } }
],
"should": [
{ "match": { "content": "Elasticsearch" } },
{ "match": { "content": "Lucene" } }
],
"minimum_should_match": 1,
"filter": [
{ "range": { "publishDate": { "gte": "2026-06-01" } } }
]
}
}
}其他查询
json
// 前缀查询
GET /articles/_search
{ "query": { "prefix": { "title": "El" } } }
// 通配符
GET /articles/_search
{ "query": { "wildcard": { "title": "Elastic*" } } }
// 模糊查询(拼写纠错)
GET /articles/_search
{ "query": { "fuzzy": { "title": "Elasticseach" } } }
// 高亮
GET /articles/_search
{
"query": { "match": { "content": "搜索引擎" } },
"highlight": {
"fields": { "content": {} },
"pre_tags": ["<em>"],
"post_tags": ["</em>"]
}
}聚合(Aggregations)
度量聚合(Metric)
json
GET /articles/_search
{
"size": 0,
"aggs": {
"total_views": { "sum": { "field": "views" } },
"avg_views": { "avg": { "field": "views" } },
"max_views": { "max": { "field": "views" } },
"min_views": { "min": { "field": "views" } },
"view_stats": { "stats": { "field": "views" } }
// stats 一次性返回 count/min/max/avg/sum
}
}桶聚合(Bucket)
json
// 按作者分组统计
GET /articles/_search
{
"size": 0,
"aggs": {
"by_author": {
"terms": { "field": "author", "size": 10, "order": { "_count": "desc" } },
"aggs": {
"avg_views": { "avg": { "field": "views" } }
}
},
"by_date": {
"date_histogram": {
"field": "publishDate",
"calendar_interval": "month",
"format": "yyyy-MM"
}
},
"by_views_range": {
"range": {
"field": "views",
"ranges": [
{ "key": "0-100", "from": 0, "to": 100 },
{ "key": "100-1000", "from": 100, "to": 1000 },
{ "key": "1000+", "from": 1000 }
]
}
}
}
}管道聚合(Pipeline)
json
GET /articles/_search
{
"size": 0,
"aggs": {
"by_month": {
"date_histogram": { "field": "publishDate", "calendar_interval": "month" },
"aggs": {
"monthly_views": { "sum": { "field": "views" } }
}
},
"monthly_moving_avg": {
"moving_fn": {
"buckets_path": "by_month>monthly_views",
"window": 3,
"script": "MovingFunctions.unweightedAvg(values)"
}
}
}
}集群架构
节点角色
| 角色 | 职责 | 说明 |
|---|---|---|
| master | 集群管理 | 维护集群元数据,轻量级 |
| data | 数据存储与查询 | 资源密集型(CPU、磁盘) |
| ingest | 数据预处理 | 写入前的管道处理 |
| ml | 机器学习 | 异常检测等 |
| coordinating | 请求路由 | 仅协调,不存数据 |
分片机制
text
一个索引 N 个分片(shard),每个分片是一个完整的 Lucene 实例:
┌─────────── articles ───────────┐
│ shard 0 │ shard 1 │ shard 2 │
│ shard 0 │ shard 1 │ shard 2 │ ← 副本
│ Lucene │ Lucene │ Lucene │
└───────────┴───────────┴─────────┘
Node A Node B Node C分片策略:
- 分片数在创建索引后不可修改(可通过 reindex)
- 建议每个分片大小 10-50 GB
- 总分片数不超过节点数 × 20
写入流程
text
写入请求 → Coordinating Node
→ 路由到主分片
→ 主分片写入 Lucene(内存 Buffer + Translog)
→ 同步到副本分片
→ 返回成功
→ 定时 refresh(默认 1s)→ 变为可搜
→ 定时 flush → 将 Buffer 写入磁盘,清空 Translog集群状态
json
// 集群健康检查
GET /_cluster/health
// green = 所有主分片和副本正常
// yellow = 所有主分片正常,但有副本未分配
// red = 有主分片未分配(数据不可用)
// 节点信息
GET /_cat/nodes?v
// 索引信息
GET /_cat/indices?v
// 分片分布
GET /_cat/shards?v性能优化
索引优化
text
- 使用 bulk API 批量写入(每批 1-15 MB)
- 写入时 refresh_interval: -1(关闭自动刷新)
- 使用多线程写入(线程数不超过 CPU 核数)
- 避免过大的 mapping(字段数 < 1000)
- 禁用不需要的 norms、doc_values查询优化
text
- 优先使用 filter 而非 query(缓存 + 不算分)
- 使用 cardinality 近似值替代精确 COUNT
- 避免深度分页(使用 search_after 替代 from+size)
- 使用 profile API 分析慢查询
- 控制结果集大小(默认只返回 10 条)索引模板
json
// 日志类索引自动管理
PUT /_index_template/logs_template
{
"index_patterns": ["logs-*"],
"template": {
"settings": {
"number_of_shards": 2,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"level": { "type": "keyword" },
"message": { "type": "text" }
}
}
},
"priority": 100
}生命周期管理(ILM)
json
// 自动管理索引生命周期:hot → warm → cold → delete
PUT /_ilm/policy/logs_policy
{
"policy": {
"phases": {
"hot": { "min_age": "0d", "actions": { "rollover": { "max_size": "50GB" } } },
"warm": { "min_age": "30d", "actions": { "shrink": { "number_of_shards": 1 } } },
"cold": { "min_age": "60d", "actions": { "freeze": {} } },
"delete": { "min_age": "90d", "actions": { "delete": {} } }
}
}
}