📚 目录导航
- 基础概念与架构
- 索引与文档操作
- 基础查询语法
- 复杂查询与 DSL
- 聚合分析
- 映射与 analyzers
- 集群与分片
- 搜索原理与评分
- 性能优化
基础篇 - 核心概念
1 Elasticsearch 是什么?
Elasticsearch 是一个基于 Lucene 的分布式、RESTful 搜索和分析引擎。专为处理海量数据而设计,支持实时搜索、日志分析、安全分析等场景。
🔍 Elasticsearch 核心定位
💡 关键特性
- 分布式架构:数据自动分片,支持 PB 级数据
- 实时搜索:数据写入后 1 秒内可被搜索到
- 全文检索:强大的 TF-IDF / BM25 相关性评分
- 多租户:一个集群可承载多个索引
- RESTful API:通过 HTTP 接口操作
2 核心概念:与 MySQL 对比理解
| 概念 | ES 术语 | MySQL 等价 | 说明 |
|---|---|---|---|
| 存储 | Index(索引) |
Database(数据库) | 逻辑命名空间,相当于表集合 |
| 存储 | Document(文档) |
Row(行) | JSON 格式的基本数据单元 |
| 字段 | Field(字段) |
Column(列) | 文档的属性 |
| 映射 | Mapping(映射) |
Schema(表结构) | 定义字段类型和分析方式 |
| 分片 | Shard(分片) |
- | 索引的水平拆分单元 |
| 副本 | Replica(副本) |
- | 分片的冗余拷贝,保证高可用 |
🏗️ ES 集群物理结构
⚠️ 重要原则
主分片数在创建索引时就固定了,后续不能改变(除非 reindex)。
副本数可以随时调整。
索引与文档操作
1 创建索引
在 ES 中创建索引就像在 MySQL 中创建数据库和表。可以指定分片数、副本数、字段映射等。
# 创建索引 - 指定分片和副本设置 PUT /products { "settings": { "number_of_shards": 3, // 主分片数,创建后不可改 "number_of_replicas": 1, // 每个主分片的副本数 "refresh_interval": "1s" // 刷新间隔,默认1s }, "mappings": { "properties": { "product_id": { "type": "keyword" }, "name": { "type": "text", "analyzer": "standard" }, "price": { "type": "float" }, "category": { "type": "keyword" }, "stock": { "type": "integer" }, "created_at": { "type": "date" }, "description": { "type": "text" } } } } # 响应 { "acknowledged": true, "shards_acknowledged": true, "index": "products" }
✅ 字段类型选择建议
keyword- 用于精确匹配、过滤、排序、聚合(不分析)text- 用于全文搜索,会被 analyzer 分词integer/float/double- 数值类型,用于范围查询、排序date- 日期类型,支持格式化boolean- 布尔值object/nested- 嵌套对象
2 文档 CRUD 操作
# ═══════════════════════════════════════════════════════════ # CREATE - 创建文档(自动生成 ID) # ═══════════════════════════════════════════════════════════ POST /products/_doc { "product_id": "P001", "name": "iPhone 15 Pro", "price": 8999.00, "category": "手机", "stock": 100, "created_at": "2024-01-15T10:30:00Z", "description": "苹果最新款智能手机,搭载A17 Pro芯片" } # 响应 { "_index": "products", "_id": "abc123", // 自动生成的 ID "_version": 1, // 版本号,用于乐观锁 "result": "created", "_seq_no": 0, "_primary_term": 1 } # ═══════════════════════════════════════════════════════════ # CREATE - 创建文档(指定 ID) # ═══════════════════════════════════════════════════════════ PUT /products/_doc/P001 { "product_id": "P001", "name": "iPhone 15 Pro", "price": 8999.00, "category": "手机" } # ═══════════════════════════════════════════════════════════ # READ - 读取文档 # ═══════════════════════════════════════════════════════════ GET /products/_doc/P001 # ═══════════════════════════════════════════════════════════ # UPDATE - 更新文档(完整替换) # ═══════════════════════════════════════════════════════════ PUT /products/_doc/P001 { "product_id": "P001", "name": "iPhone 15 Pro Max", // 修改了 name "price": 9999.00, // 修改了 price "category": "手机" } # ═══════════════════════════════════════════════════════════ # PARTIAL UPDATE - 部分更新(只更新指定字段) # ═══════════════════════════════════════════════════════════ POST /products/_update/P001 { "doc": { "price": 8799.00, // 只更新价格 "stock": 80 // 只更新库存 } } # ═══════════════════════════════════════════════════════════ # DELETE - 删除文档 # ═══════════════════════════════════════════════════════════ DELETE /products/_doc/P001
基础查询语法
1 Search API 基础
# ═══════════════════════════════════════════════════════════ # 方式一:URI Search(简单参数查询) # ═══════════════════════════════════════════════════════════ GET /products/_search?q=name:iPhone&size=10 # ═══════════════════════════════════════════════════════════ # 方式二:Request Body Search(推荐,功能更强大) # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { /* 查询条件 */ }, "from": 0, // 起始位置(分页用) "size": 10, // 返回数量 "sort": [ // 排序 { "price": "asc" }, { "_score": "desc" } ], "_source": ["name", "price", "category"] // 指定返回字段 } # ═══════════════════════════════════════════════════════════ # 返回结构解读 # ═══════════════════════════════════════════════════════════ { "took": 15, // 查询耗时(毫秒) "timed_out": false, // 是否超时 "hits": { "total": { "value": 42, "relation": "eq" }, "max_score": 2.5, "hits": [ // 实际文档数组 { "_index": "products", "_id": "P001", "_score": 2.5, // 相关性评分 "_source": { /* 文档内容 */ } } ] } }
2 基础查询类型
# ═══════════════════════════════════════════════════════════ # 1. match_all - 匹配所有文档 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "match_all": {} } } # ═══════════════════════════════════════════════════════════ # 2. match - 全文搜索(会被分析器分词) # 搜索 "iPhone 手机",会分词为 ["iphone", "手机"] 分别匹配 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "match": { "name": "iPhone 手机" } } } # match + operator(多词查询) GET /products/_search { "query": { "match": { "name": { "query": "iPhone Pro", "operator": "and" // 必须包含所有词 } } } } # ═══════════════════════════════════════════════════════════ # 3. term - 精确值查询(不分词,用于 keyword/numeric/date) # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "term": { "category": "手机" // 精确匹配,不分词 } } } # terms - 多值精确匹配 GET /products/_search { "query": { "terms": { "category": ["手机", "电脑"] } } } # ═══════════════════════════════════════════════════════════ # 4. range - 范围查询 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "range": { "price": { "gte": 1000, // >= 1000 "lte": 5000 // <= 5000 } } } } # ═══════════════════════════════════════════════════════════ # 5. bool - 布尔组合查询 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "bool": { "must": [ // 必须满足(AND,计分) { "match": { "name": "iPhone" } } ], "should": [ // 满足加分(OR) { "term": { "category": "手机" } } ], "filter": [ // 必须满足(AND,不计分) { "range": { "price": { "lte": 10000 } } } ], "must_not": [ // 必须不满足(NOT) { "term": { "stock": 0 } } ] } } }
💡 must vs filter 的区别
must:计算相关性评分(_score),影响排序
filter:只做过滤,不计算评分,执行速度更快(利用缓存)
中级篇 - 复杂查询与 DSL
1 高级查询子句
# ═══════════════════════════════════════════════════════════ # multi_match - 多字段匹配 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "multi_match": { "query": "iPhone 手机", "fields": ["name^2", "description", "category"], "type": "best_fields", // 取最高分字段 "tie_breaker": 0.3 // 其他字段的权重 } } } # multi_match 类型说明: # - best_fields: 取最高分(默认) # - most_fields: 累加所有字段分数 # - cross_fields: 跨字段匹配(分词跨字段) # - phrase: 短语匹配 # - phrase_prefix: 前缀短语匹配 # ═══════════════════════════════════════════════════════════ # match_phrase - 短语匹配(保持顺序) # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "match_phrase": { "description": { "query": "A17 Pro 芯片", "slop": 2 // 允许词序移动的距离 } } } } # ═══════════════════════════════════════════════════════════ # prefix - 前缀查询 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "prefix": { "product_id": "P00" // 匹配 P001, P002... } } } # ═══════════════════════════════════════════════════════════ # wildcard - 通配符查询 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "wildcard": { "product_id": "P*" // 匹配任何以 P 开头的 } } } # ═══════════════════════════════════════════════════════════ # exists - 存在性查询 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "exists": { "field": "description" // 返回有 description 字段的文档 } } } # ═══════════════════════════════════════════════════════════ # ids - ID 查询 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "ids": { "values": ["P001", "P002", "P003"] } } }
2 嵌套查询与父子文档
📦 Object vs Nested 对比
# ═══════════════════════════════════════════════════════════ # 定义 nested 映射 # ═══════════════════════════════════════════════════════════ PUT /products { "mappings": { "properties": { "name": { "type": "text" }, "variants": { "type": "nested", // 嵌套类型 "properties": { "color": { "type": "keyword" }, "size": { "type": "keyword" }, "stock": { "type": "integer" } } } } } } # ═══════════════════════════════════════════════════════════ # nested 查询 - 查询同一嵌套对象内的多个条件 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "nested": { "path": "variants", "query": { "bool": { "must": [ { "term": { "variants.color": "黑色" } }, { "term": { "variants.size": "256G" } } ] } } } } }
聚合分析
1 聚合查询基础
📊 ES 聚合分类
# ═══════════════════════════════════════════════════════════ # 1. Metrics 聚合 - 单值指标 # ═══════════════════════════════════════════════════════════ GET /products/_search { "size": 0, // 不返回文档,只返回聚合结果 "aggs": { "avg_price": { // 聚合名称 "avg": { "field": "price" } }, "max_price": { "max": { "field": "price" } }, "min_price": { "min": { "field": "price" } }, "sum_price": { "sum": { "field": "price" } }, "total_docs": { "value_count": { "field": "product_id" } } } } # ═══════════════════════════════════════════════════════════ # 2. Bucket 聚合 - 分桶(类似 GROUP BY) # ═══════════════════════════════════════════════════════════ GET /products/_search { "size": 0, "aggs": { "by_category": { "terms": { "field": "category", // 按类别分桶 "size": 10 // 返回前 10 个桶 } }, "price_ranges": { "range": { "field": "price", "ranges": [ { "key": "廉价", "from": 0, "to": 1000 }, { "key": "中端", "from": 1000, "to": 5000 }, { "key": "高端", "from": 5000 } ] } } } } # ═══════════════════════════════════════════════════════════ # 3. 多层嵌套聚合(桶 + 子聚合) # ═══════════════════════════════════════════════════════════ GET /products/_search { "size": 0, "aggs": { "by_category": { "terms": { "field": "category" }, "aggs": { // 子聚合 "avg_price": { "avg": { "field": "price" } }, "price_stats": { "stats": { "field": "price" } } } } } }
2 常用聚合类型详解
| 聚合类型 | 说明 | 示例 |
|---|---|---|
avg / sum / min / max |
基本数值统计 | 平均价格、总销量、最高分 |
stats |
一次性返回所有统计 | count, min, max, avg, sum |
cardinality |
去重计数(近似) | UV 统计 |
percentiles |
百分位数 | P50, P90, P99 响应时间 |
percentile_ranks |
百分位排名 | 有多少比例低于某值 |
histogram |
直方图(数值间隔) | 价格每 500 分组 |
date_histogram |
日期直方图 | 按天/月/年分组 |
top_hits |
返回桶内高分文档 | 每个类别销量前3 |
# ═══════════════════════════════════════════════════════════ # cardinality - 去重计数(HyperLogLog 算法,近似值) # ═══════════════════════════════════════════════════════════ GET /access_logs/_search { "size": 0, "aggs": { "unique_users": { "cardinality": { "field": "user_id" } } } } # ═══════════════════════════════════════════════════════════ # percentiles - 百分位数(TDigest 算法,近似值) # ═══════════════════════════════════════════════════════════ GET /api_logs/_search { "size": 0, "aggs": { "latency_percentiles": { "percentiles": { "field": "latency_ms", "percents": [50, 75, 90, 95, 99] } } } } # ═══════════════════════════════════════════════════════════ # histogram - 价格分布直方图 # ═══════════════════════════════════════════════════════════ GET /products/_search { "size": 0, "aggs": { "price_distribution": { "histogram": { "field": "price", "interval": 500, // 每 500 一个桶 "min_doc_count": 1 // 最少文档数 } } } } # ═══════════════════════════════════════════════════════════ # date_histogram - 按月统计销售趋势 # ═══════════════════════════════════════════════════════════ GET /orders/_search { "size": 0, "aggs": { "monthly_sales": { "date_histogram": { "field": "order_date", "calendar_interval": "month", "format": "yyyy-MM" }, "aggs": { "revenue": { "sum": { "field": "amount" } } } } } }
映射与分析器
1 分词器(Analyzer)原理
🔍 分词流程解析
# ═══════════════════════════════════════════════════════════ # 内置分词器对比 # ═══════════════════════════════════════════════════════════ # standard (默认) - 按空格/标点切分,小写化 # "iPhone 15 Pro!" → ["iphone", "15", "pro"] # simple - 按字母切分,非字母字符分隔,小写化 # "iPhone-15" → ["iphone", "15"] # whitespace - 按空格切分,不小写化 # "iPhone 15 Pro" → ["iPhone", "15", "Pro"] # keyword - 不分词,整个字符串作为一个词 # "iPhone 15 Pro" → ["iPhone 15 Pro"] # ═══════════════════════════════════════════════════════════ # 自定义分词器 # ═══════════════════════════════════════════════════════════ PUT /my_index { "settings": { "analysis": { "char_filter": { "my_char_filter": { "type": "mapping", "mappings": [ "< => and", // HTML 实体转换 "> => or" ] } }, "filter": { "my_synonym": { "type": "synonym", "synonyms": [ "手机, phone, 移动电话", "电脑, computer, PC" ] }, "my_stemmer": { "type": "stemmer", "language": "english" } }, "analyzer": { "my_analyzer": { "type": "custom", "char_filter": ["my_char_filter"], "tokenizer": "standard", "filter": ["lowercase", "my_synonym", "my_stemmer"] } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "my_analyzer" } } } } # ═══════════════════════════════════════════════════════════ # IK 中文分词器示例(需要安装插件) # ═══════════════════════════════════════════════════════════ # 安装:./bin/elasticsearch-plugin install analysis-ik PUT /cn_index { "mappings": { "properties": { "title": { "type": "text", "analyzer": "ik_max_word", // 细粒度分词 "search_analyzer": "ik_smart" // 搜索时分词 } } } } # "中华人民共和国人民" → # ik_max_word: [中华, 中华人民共和国, 人民, 共和国, 国, 人, 民] # ik_smart: [中华人民共和国, 人民]
高级篇 - 集群与架构
1 集群原理与分片分配
🔄 分片分配流程
# ═══════════════════════════════════════════════════════════ # 集群健康状态 # ═══════════════════════════════════════════════════════════ GET /_cluster/health # 响应: # { # "cluster_name": "my_cluster", # "status": "green", // green/yellow/red # "number_of_nodes": 3, # "number_of_data_nodes": 3, # "active_shards": 45, # "relocating_shards": 0, # "active_primary_shards": 15 # } # ═══════════════════════════════════════════════════════════ # 动态调整副本数 # ═══════════════════════════════════════════════════════════ # 提高可用性 - 增加副本 PUT /products/_settings { "number_of_replicas": 2 } # ═══════════════════════════════════════════════════════════ # 手动分片重新分配 # ═══════════════════════════════════════════════════════════ # 查看分片分配情况 GET /_cat/shards?v # 手动移动分片(重新均衡) POST /_cluster/reroute { "commands": [ { "move": { "index": "products", "shard": 0, "from_node": "node1", "to_node": "node2" } } ] } # ═══════════════════════════════════════════════════════════ # 集群级别设置 # ═══════════════════════════════════════════════════════════ PUT /_cluster/settings { "persistent": { "cluster.routing.allocation.cluster_concurrent_rebalance": 2 }, "transient": { "indices.recovery.max_bytes_per_sec": "100mb" } }
搜索原理与评分机制
1 TF-IDF 与 BM25 算法
📐 相关性评分公式
# ═══════════════════════════════════════════════════════════ # field_value_factor - 用字段值影响评分 # 评分 = 原始评分 × popularity^0.5 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "function_score": { "query": { "match": { "name": "iPhone" } }, "field_value_factor": { "field": "popularity", "factor": 1.2, "modifier": "sqrt", // sqrt/half/log/square/none "missing": 1 } } } } # ═══════════════════════════════════════════════════════════ # boost - 字段权重提升 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "multi_match": { "query": "iPhone 手机", "fields": [ "name^3", // name 权重 3 "description^1", // description 权重 1 "category^2" // category 权重 2 ] } } } # ═══════════════════════════════════════════════════════════ # explain - 查看详细评分 # ═══════════════════════════════════════════════════════════ GET /products/_explain/P001 { "query": { "match": { "name": "iPhone" } } }
性能优化
1 查询优化技巧
| 优化项 | 方法 | 效果 |
|---|---|---|
| filter 缓存 | 用 filter 替代 must | 避免重复计算评分 |
| _source 过滤 | 只返回必要字段 | 减少网络传输 |
| 禁用 scoring | constant_score + filter | 跳过评分计算 |
| 分页深度 | 避免 deep pagination | 防止内存爆炸 |
| 路由优化 | 使用 routing 参数 | 减少搜索分片 |
# ═══════════════════════════════════════════════════════════ # 1. 只返回需要的字段(_source filtering) # ═══════════════════════════════════════════════════════════ GET /products/_search { "_source": { "includes": ["name", "price", "category"], // 只返回这些字段 "excludes": ["description"] // 排除这些字段 }, "query": { "match_all": {} } } # ═══════════════════════════════════════════════════════════ # 2. constant_score - 不需要评分的查询 # ═══════════════════════════════════════════════════════════ GET /products/_search { "query": { "constant_score": { "filter": { "term": { "status": "active" } }, "boost": 1.0 } } } # ═══════════════════════════════════════════════════════════ # 3. 路由优化 - 直接定位分片 # ═══════════════════════════════════════════════════════════ # 写入时指定 routing(通常用 user_id) POST /orders/_doc?routing=user_123 { "user_id": "user_123", "amount": 299, "product": "iPhone" } # 查询时使用相同 routing,只搜索一个分片 GET /orders/_search?routing=user_123 { "query": { "term": { "user_id": "user_123" } } } # ═══════════════════════════════════════════════════════════ # 4. 分页优化 - Search After(深度分页推荐) # ═══════════════════════════════════════════════════════════ # 第一页 GET /products/_search { "size": 10, "sort": [ { "price": "asc" }, { "_id": "asc" } ], "query": { "match_all": {} } } # 获取最后一页的 sort 值 # 第二页(使用 search_after) GET /products/_search { "size": 10, "sort": [ { "price": "asc" }, { "_id": "asc" } ], "search_after": [2999, "P005"], // 上一页最后的 sort 值 "query": { "match_all": {} } } # ═══════════════════════════════════════════════════════════ # 5. 预热查询 - 让热点数据进入 filesystem cache # ═══════════════════════════════════════════════════════════ # 注册 searcher 预热 POST /products/_warmup { "query": { "bool": { "should": [ { "term": { "category": "手机" } }, { "term": { "category": "电脑" } } ] } } }
2 索引优化与别名
# ═══════════════════════════════════════════════════════════ # 1. Index Alias - 索引别名(零停机切换) # ═══════════════════════════════════════════════════════════ # 创建别名指向索引 POST /_aliases { "actions": [ { "add": { "index": "products_v1", "alias": "products" } } ] } # 创建新索引并切换别名(零停机 reindex) POST /_aliases { "actions": [ { "remove": { "index": "products_v1", "alias": "products" } }, { "add": { "index": "products_v2", "alias": "products" } } ] } # ═══════════════════════════════════════════════════════════ # 2. Reindex - 数据重建索引 # ═══════════════════════════════════════════════════════════ POST /_reindex { "source": { "index": "products_v1", "size": 5000 }, "dest": { "index": "products_v2" } } # ═══════════════════════════════════════════════════════════ # 3. Force Merge - 段合并(减少 segments) # ═══════════════════════════════════════════════════════════ # 合并后每个分片最多 1 个 segment,提升查询性能 POST /products/_forcemerge?max_num_segments=1 # ═══════════════════════════════════════════════════════════ # 4. Refresh vs Flush # ═══════════════════════════════════════════════════════════ # refresh - 使最近写入可被搜索(默认1s) # flush - 将 translog 刷盘,清空 translog,生成新 segment # 批量写入后手动 refresh(需要立即可查) POST /products/_bulk { "index": { "_id": "1" } } { "name": "iPhone" } { "index": { "_id": "2" } } { "name": "Samsung" } POST /products/_refresh // 使之立即可搜索 # ═══════════════════════════════════════════════════════════ # 5. 索引模板 - 自动应用配置 # ═══════════════════════════════════════════════════════════ PUT /_index_template/logs_template { "index_patterns": ["logs-*"], "template": { "settings": { "number_of_shards": 3, "number_of_replicas": 1 }, "mappings": { "properties": { "timestamp": { "type": "date" }, "level": { "type": "keyword" }, "message": { "type": "text" } } } } }
3 集群调优参数
| 参数 | 默认值 | 优化建议 |
|---|---|---|
indices.memory.index_buffer_size |
10% | 写入量大时提高到 20-30% |
indices.queries.cache.size |
10% | 查询命中率高时提高 |
thread_pool.write |
核心数 | 写入瓶颈时可增大 |
thread_pool.search |
核心数×3 | 查询瓶颈时可增大 |
indices.refresh_interval |
1s | 写入量大时改为 5s 或 -1(手动刷新) |
# ═══════════════════════════════════════════════════════════ # 集群级别优化配置 # ═══════════════════════════════════════════════════════════ PUT /_cluster/settings { "persistent": { # 写入优化 "indices.memory.index_buffer_size": "20%", "indices.refresh.interval": "5s", "indices.store.throttle.max_bytes_per_sec": "150mb", # 查询优化 "indices.queries.cache.size": "15%", "thread_pool.write.queue_size": 1000, "thread_pool.search.queue_size": 1000 } } # ═══════════════════════════════════════════════════════════ # 查看节点统计信息 # ═══════════════════════════════════════════════════════════ GET /_nodes/stats # 查看索引统计 GET /products/_stats # 查看 hot threads(排查慢查询) GET /_nodes/hot_threads
恭喜你完成 ES 完整教程!
基础 ✓
中级 ✓
高级 ✓