es 安装
elasticsearch(理解为库) kibana(理解为连接工具)
es 和 kibana(5601) 的版本要保持一致
MySQL 对照学习 Elasticsearch(ES)
术语对照
| MySQL | Elasticsearch |
|---|---|
| database | index(索引) |
| table | type(7.x 起固定为 _doc,8.x 彻底移除多 type) |
| row | document(文档) |
| column | field(字段) |
| schema | mapping(映射) |
| sql | DSL(Domain Specific Language 查询语法) |
说明:自 ES 7.x 起,一个索引只能有一个 type,名称通常为 _doc;8.x 中 type 概念对外基本不可见,建模时直接面向「索引 + 映射」。
核心概念速览
- 索引(index):
- 类似数据库中的「库」,同一类文档的逻辑集合,内部按分片(primary shard)和副本(replica shard)存储。
- 文档(document):
- 类似一行数据,JSON 对象,通过
_id唯一标识,可由 ES 自动生成或自定义。 - 字段(field):
- 文档的属性,类似列。字段类型影响倒排索引的建立方式和可用查询。
- 映射(mapping):
- 类似表结构定义,声明字段类型与索引、分词方式等。映射一旦发布,字段类型基本不可变更(需要重建索引并重导数据)。
- 分词与分析器(analyzer):
- 文本如何被切分成 term 并写入倒排索引,决定全文检索效果(中文常用
ik_max_word/ik_smart等第三方插件)。
建模指南(对照 MySQL 思维)
- 先确定查询维度与检索方式,再设计字段与映射;不要照搬 MySQL 的第三范式。
- 适度冗余、去 join 化:ES 没有跨索引 join,查询以单索引为单位;复杂场景用 denormalization 或 nested/parent-child。
- 数值、时间、keyword 与 text 区分清楚:
keyword:精确匹配、聚合、排序;text:全文检索(会分词),不适合聚合排序;- 时间用
date,地理位置用geo_point等专用类型。
常见操作对照
- 创建索引(含映射)
SQL(建库/建表/字段):
-- MySQL 示例
CREATE DATABASE shop;
CREATE TABLE product (
id BIGINT PRIMARY KEY,
title VARCHAR(255),
price DECIMAL(10,2),
tags JSON
);
ES(建索引 + 映射):
PUT /shop_product
{
"settings": {"number_of_shards": 1, "number_of_replicas": 1},
"mappings": {
"properties": {
"title": {"type": "text", "analyzer": "standard"},
"price": {"type": "double"},
"tags": {"type": "keyword"},
"createdAt": {"type": "date"}
}
}
}
- 写入一行/文档
SQL:
INSERT INTO product(id,title,price) VALUES(1,'iPhone',5999.00);
ES:
POST /shop_product/_doc/1
{
"title": "iPhone",
"price": 5999.00,
"tags": ["phone", "apple"],
"createdAt": "2025-09-18T12:00:00Z"
}
- 按主键查询
SQL:
SELECT * FROM product WHERE id=1;
ES:
GET /shop_product/_doc/1
- 条件查询(DSL vs SQL)
SQL:
SELECT id,title FROM product
WHERE price BETWEEN 3000 AND 8000 AND title LIKE '%phone%'
ORDER BY price DESC LIMIT 10 OFFSET 0;
ES:
POST /shop_product/_search
{
"from": 0,
"size": 10,
"sort": [{"price": "desc"}],
"_source": ["id","title","price"],
"query": {
"bool": {
"must": [ {"match": {"title": "phone"}} ],
"filter": [ {"range": {"price": {"gte": 3000, "lte": 8000}}} ]
}
}
}
- 更新与删除
SQL:UPDATE ... WHERE id=? / DELETE FROM ... WHERE id=?
ES:
POST /shop_product/_update/1
{"doc": {"price": 5799}}
DELETE /shop_product/_doc/1
- 聚合(GROUP BY 对照)
SQL:
SELECT tags, COUNT(*) AS cnt FROM product GROUP BY tags;
ES:
POST /shop_product/_search
{
"size": 0,
"aggs": {
"by_tag": {"terms": {"field": "tags"}}
}
}
索引生命周期与性能要点
- 分片数在创建索引时确定,后续只能通过重建索引调整;副本数可在线调整。
- 写多读少场景:降低副本、提高刷新间隔;读多:适当增加副本、开启缓存与合适的字段
doc_values。 - 大改映射/类型时采用重建索引(reindex):新索引 -> 导数据 -> 切别名。
Kibana 与端口
- Kibana 默认端口 5601,版本需与 ES 保持一致(7.x 对 7.x,8.x 对 8.x)。
- 在 Kibana Dev Tools 中可直接粘贴上面的 REST/DSL 示例执行。
我们主要是用 es 的查询功能
GET _search?q=bobby // 会查询所有的index
通过 request body 来查询
Request Body 查询详解
ES 支持两种查询方式:
- URL 参数查询:
GET _search?q=field:value(简单快速) - Request Body 查询:
POST _search+ JSON body(功能强大,推荐)
为什么推荐 Request Body 查询?
- 功能完整:支持复杂查询、聚合、排序、分页等
- 可读性强:JSON 结构清晰,便于维护
- 性能更好:避免 URL 长度限制,支持更复杂的查询逻辑
- 调试友好:Kibana Dev Tools 中直接粘贴执行
基础查询示例
- 简单匹配查询
POST /shop_product/_search
{
"query": {
"match": {
"title": "iPhone"
}
}
}
- 多条件组合查询
POST /shop_product/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "phone"}},
{"range": {"price": {"gte": 1000, "lte": 8000}}}
],
"must_not": [
{"term": {"status": "discontinued"}}
],
"should": [
{"match": {"tags": "apple"}}
]
}
}
}
- 精确匹配 vs 全文搜索
# 精确匹配(不分词)
POST /shop_product/_search
{
"query": {
"term": {
"tags": "phone"
}
}
}
# 全文搜索(会分词)
POST /shop_product/_search
{
"query": {
"match": {
"title": "iPhone 15 Pro"
}
}
}
- 分页与排序
POST /shop_product/_search
{
"from": 0,
"size": 10,
"sort": [
{"price": {"order": "desc"}},
{"_score": {"order": "desc"}}
],
"query": {
"match_all": {}
}
}
- 指定返回字段
POST /shop_product/_search
{
"_source": ["title", "price", "tags"],
"query": {
"match_all": {}
}
}
- 聚合查询(统计)
POST /shop_product/_search
{
"size": 0,
"aggs": {
"price_stats": {
"stats": {"field": "price"}
},
"tags_count": {
"terms": {"field": "tags", "size": 10}
},
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{"to": 1000},
{"from": 1000, "to": 5000},
{"from": 5000}
]
}
}
}
}
- 高亮显示
POST /shop_product/_search
{
"query": {
"match": {"title": "iPhone"}
},
"highlight": {
"fields": {
"title": {}
}
}
}
常用查询类型对照
| 需求 | SQL | ES Request Body |
|---|---|---|
| 全表扫描 | SELECT * FROM table |
{"query": {"match_all": {}}} |
| 精确匹配 | WHERE id = 1 |
{"query": {"term": {"id": 1}}} |
| 模糊匹配 | WHERE title LIKE '%phone%' |
{"query": {"match": {"title": "phone"}}} |
| 范围查询 | WHERE price BETWEEN 1000 AND 5000 |
{"query": {"range": {"price": {"gte": 1000, "lte": 5000}}}} |
| 多条件 AND | WHERE a=1 AND b=2 |
{"query": {"bool": {"must": [{"term": {"a": 1}}, {"term": {"b": 2}}]}}} |
| 多条件 OR | WHERE a=1 OR b=2 |
{"query": {"bool": {"should": [{"term": {"a": 1}}, {"term": {"b": 2}}]}}} |
| 分组统计 | SELECT tag, COUNT(*) FROM table GROUP BY tag |
{"aggs": {"by_tag": {"terms": {"field": "tag"}}}} |
性能优化建议
- 使用 filter 而非 query:filter 不计算相关性分数,性能更好
POST /shop_product/_search
{
"query": {
"bool": {
"filter": [
{"range": {"price": {"gte": 1000}}}
]
}
}
}
- 合理使用 size:避免一次性返回大量数据
- 使用 _source 过滤:只返回需要的字段
- 缓存常用查询:ES 会自动缓存 filter 查询结果
POST 更新操作详解
ES 中的更新操作有两种方式:覆盖更新和部分更新,理解它们的区别很重要。
1. 覆盖更新(PUT 方式)
特点:完全替换整个文档,未指定的字段会被删除
# 原始文档
{
"id": 1,
"title": "iPhone 15",
"price": 5999,
"tags": ["phone", "apple"],
"description": "最新款iPhone",
"stock": 100
}
# 覆盖更新(只保留指定的字段)
PUT /shop_product/_doc/1
{
"title": "iPhone 15 Pro",
"price": 7999
}
# 更新后的文档(description 和 stock 字段被删除)
{
"id": 1,
"title": "iPhone 15 Pro",
"price": 7999
}
2. 部分更新(POST _update 方式)
特点:只更新指定字段,其他字段保持不变
# 原始文档
{
"id": 1,
"title": "iPhone 15",
"price": 5999,
"tags": ["phone", "apple"],
"description": "最新款iPhone",
"stock": 100
}
# 部分更新(只更新指定字段)
POST /shop_product/_update/1
{
"doc": {
"title": "iPhone 15 Pro",
"price": 7999
}
}
# 更新后的文档(其他字段保持不变)
{
"id": 1,
"title": "iPhone 15 Pro",
"price": 7999,
"tags": ["phone", "apple"],
"description": "最新款iPhone",
"stock": 100
}
3. 高级更新操作
3.1 条件更新(upsert)
如果文档不存在则创建,存在则更新:
POST /shop_product/_update/999
{
"doc": {
"title": "新商品",
"price": 1000
},
"upsert": {
"title": "新商品",
"price": 1000,
"tags": ["new"],
"created_at": "2025-01-18"
}
}
3.2 脚本更新
使用脚本进行复杂更新:
# 增加库存
POST /shop_product/_update/1
{
"script": {
"source": "ctx._source.stock += params.increment",
"params": {
"increment": 50
}
}
}
# 条件更新(只有价格大于5000才更新)
POST /shop_product/_update/1
{
"script": {
"source": "if (ctx._source.price > 5000) { ctx._source.price = params.new_price }",
"params": {
"new_price": 7500
}
}
}
3.3 数组操作
# 添加标签
POST /shop_product/_update/1
{
"script": {
"source": "if (ctx._source.tags == null) { ctx._source.tags = [] } ctx._source.tags.add(params.tag)",
"params": {
"tag": "premium"
}
}
}
# 移除标签
POST /shop_product/_update/1
{
"script": {
"source": "ctx._source.tags.removeIf(item -> item == params.tag)",
"params": {
"tag": "old"
}
}
}
4. 批量更新
POST /shop_product/_bulk
{"update":{"_id":"1"}}
{"doc":{"price":5999}}
{"update":{"_id":"2"}}
{"doc":{"price":6999}}
{"update":{"_id":"3"}}
{"doc":{"price":7999}}
5. 更新操作对比表
| 操作方式 | 方法 | 特点 | 适用场景 |
|---|---|---|---|
| 覆盖更新 | PUT /index/_doc/id |
完全替换文档 | 文档结构变化大,需要删除字段 |
| 部分更新 | POST /index/_update/id |
只更新指定字段 | 日常业务更新,保留其他字段 |
| 条件更新 | POST /index/_update/id + upsert |
存在则更新,不存在则创建 | 不确定文档是否存在 |
| 脚本更新 | POST /index/_update/id + script |
复杂逻辑更新 | 需要计算、条件判断的更新 |
6. 性能注意事项
- 部分更新性能更好:只传输变更的字段,减少网络开销
- 脚本更新较慢:需要解析和执行脚本,性能相对较低
- 批量操作:大量更新时使用
_bulkAPI 提高效率 - 版本控制:ES 自动处理并发更新冲突,通过
_version字段
删除数据操作详解
ES 中的删除操作有多种方式,从单个文档删除到整个索引删除,理解不同场景下的删除方法很重要。
1. 删除单个文档
1.1 按 ID 删除
# 删除指定 ID 的文档
DELETE /shop_product/_doc/1
# 响应示例
{
"_index": "shop_product",
"_id": "1",
"_version": 2,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
1.2 条件删除(通过查询)
# 删除价格小于 1000 的所有商品
POST /shop_product/_delete_by_query
{
"query": {
"range": {
"price": {
"lt": 1000
}
}
}
}
# 删除包含特定标签的商品
POST /shop_product/_delete_by_query
{
"query": {
"term": {
"tags": "discontinued"
}
}
}
2. 批量删除
2.1 使用 _bulk API
POST /shop_product/_bulk
{"delete":{"_id":"1"}}
{"delete":{"_id":"2"}}
{"delete":{"_id":"3"}}
2.2 批量条件删除
# 删除多个条件的商品
POST /shop_product/_delete_by_query
{
"query": {
"bool": {
"should": [
{"term": {"status": "discontinued"}},
{"range": {"last_updated": {"lt": "2020-01-01"}}}
]
}
}
}
3. 删除整个索引
# 删除整个索引(危险操作!)
DELETE /shop_product
# 响应示例
{
"acknowledged": true
}
4. 删除索引中的类型(7.x 以下版本)
# 删除索引中的特定类型(仅适用于 6.x 及以下版本)
DELETE /shop_product/product_type
5. 高级删除操作
5.1 带版本控制的删除
# 只有版本匹配时才删除(防止并发删除)
DELETE /shop_product/_doc/1?version=1&version_type=external
5.2 异步删除大量数据
# 异步删除(适用于大量数据)
POST /shop_product/_delete_by_query
{
"query": {
"match_all": {}
},
"wait_for_completion": false,
"conflicts": "proceed"
}
# 响应包含任务 ID,可用于查询删除进度
{
"task": "r1A2WoRbTwKZ516z6NEs5A:36619"
}
# 查询任务状态
GET /_tasks/r1A2WoRbTwKZ516z6NEs5A:36619
5.3 删除时保留快照
# 删除前创建快照(备份)
PUT /_snapshot/backup_repo/snapshot_before_delete
{
"indices": "shop_product",
"ignore_unavailable": true,
"include_global_state": false
}
# 然后执行删除操作
POST /shop_product/_delete_by_query
{
"query": {
"range": {
"created_at": {
"lt": "2020-01-01"
}
}
}
}
6. 删除操作对比表
| 删除方式 | 方法 | 特点 | 适用场景 |
|---|---|---|---|
| 按 ID 删除 | DELETE /index/_doc/id |
精确删除单个文档 | 已知文档 ID 的删除 |
| 条件删除 | POST /index/_delete_by_query |
根据查询条件删除 | 批量删除符合条件的文档 |
| 批量删除 | POST /index/_bulk |
一次删除多个指定文档 | 已知多个文档 ID 的删除 |
| 删除索引 | DELETE /index |
删除整个索引 | 清理测试数据或重建索引 |
| 异步删除 | _delete_by_query + wait_for_completion:false |
不阻塞,后台执行 | 删除大量数据时避免超时 |
7. 删除操作注意事项
7.1 性能考虑
# 大量删除时使用滚动查询提高性能
POST /shop_product/_delete_by_query
{
"query": {
"range": {
"price": {
"lt": 100
}
}
},
"scroll_size": 1000,
"conflicts": "proceed"
}
7.2 安全删除
# 删除前先查询确认
POST /shop_product/_search
{
"query": {
"range": {
"price": {
"lt": 100
}
}
},
"size": 0
}
# 确认无误后再执行删除
POST /shop_product/_delete_by_query
{
"query": {
"range": {
"price": {
"lt": 100
}
}
}
}
7.3 删除监控
# 监控删除进度
GET /_tasks?detailed=true&actions=*delete*
# 取消删除任务
POST /_tasks/task_id/_cancel
8. 删除 vs 软删除
在实际业务中,通常使用软删除而不是物理删除:
# 软删除:标记为已删除
POST /shop_product/_update/1
{
"doc": {
"deleted": true,
"deleted_at": "2025-01-18T12:00:00Z"
}
}
# 查询时排除已删除的文档
POST /shop_product/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "iPhone"}}
],
"must_not": [
{"term": {"deleted": true}}
]
}
}
}
9. 恢复已删除的数据
ES 删除的数据无法直接恢复,但可以通过以下方式:
- 从快照恢复:如果之前创建了快照
- 从备份恢复:如果有数据备份
- 重新导入:从原始数据源重新导入
# 从快照恢复索引
POST /_snapshot/backup_repo/snapshot_name/_restore
{
"indices": "shop_product",
"ignore_unavailable": true,
"include_global_state": false
}
批量插入操作详解
ES 中的批量插入是处理大量数据的高效方式,通过 _bulk API 可以一次性执行多个操作,包括插入、更新、删除等。
1. 基础批量插入
1.1 使用 _bulk API 插入
POST /shop_product/_bulk
{"index":{"_id":"1"}}
{"title":"iPhone 15","price":5999,"tags":["phone","apple"],"stock":100}
{"index":{"_id":"2"}}
{"title":"Samsung Galaxy","price":4999,"tags":["phone","android"],"stock":50}
{"index":{"_id":"3"}}
{"title":"MacBook Pro","price":12999,"tags":["laptop","apple"],"stock":20}
1.2 自动生成 ID 的批量插入
POST /shop_product/_bulk
{"index":{}}
{"title":"iPad Air","price":3999,"tags":["tablet","apple"],"stock":30}
{"index":{}}
{"title":"Dell XPS","price":8999,"tags":["laptop","windows"],"stock":15}
{"index":{}}
{"title":"Surface Pro","price":6999,"tags":["tablet","windows"],"stock":25}
2. 混合批量操作
POST /shop_product/_bulk
{"index":{"_id":"10"}}
{"title":"新商品1","price":1000,"tags":["new"],"stock":100}
{"update":{"_id":"1"}}
{"doc":{"price":5799}}
{"delete":{"_id":"2"}}
{"index":{"_id":"11"}}
{"title":"新商品2","price":2000,"tags":["new"],"stock":200}
3. 批量插入响应处理
# 批量操作响应示例
{
"took": 30,
"errors": false,
"items": [
{
"index": {
"_index": "shop_product",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"status": 201
}
},
{
"index": {
"_index": "shop_product",
"_id": "2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"status": 201
}
}
]
}
4. 错误处理
4.1 检查批量操作中的错误
# 包含错误的批量操作
POST /shop_product/_bulk
{"index":{"_id":"1"}}
{"title":"商品1","price":1000}
{"index":{"_id":"1"}} # 重复ID,会产生错误
{"title":"商品2","price":2000}
# 响应中的错误信息
{
"took": 5,
"errors": true,
"items": [
{
"index": {
"_index": "shop_product",
"_id": "1",
"status": 201,
"result": "created"
}
},
{
"index": {
"_index": "shop_product",
"_id": "1",
"status": 409,
"error": {
"type": "version_conflict_engine_exception",
"reason": "[1]: version conflict, document already exists"
}
}
}
]
}
4.2 处理部分失败的情况
# 使用 filter_path 只返回错误项
POST /shop_product/_bulk?filter_path=items.*.error
{"index":{"_id":"1"}}
{"title":"商品1","price":1000}
{"index":{"_id":"1"}}
{"title":"商品2","price":2000}
5. 性能优化
5.1 批量大小控制
# 推荐的批量大小:1000-5000 个文档
POST /shop_product/_bulk
{"index":{"_id":"1"}}
{"title":"商品1","price":1000}
# ... 更多文档
{"index":{"_id":"1000"}}
{"title":"商品1000","price":1000}
5.2 刷新策略
# 批量插入时不立即刷新(提高性能)
POST /shop_product/_bulk?refresh=false
{"index":{"_id":"1"}}
{"title":"商品1","price":1000}
{"index":{"_id":"2"}}
{"title":"商品2","price":2000}
# 批量插入后手动刷新
POST /shop_product/_refresh
5.3 并发控制
# 设置批量操作的超时时间
POST /shop_product/_bulk?timeout=60s
{"index":{"_id":"1"}}
{"title":"商品1","price":1000}
6. 从文件批量导入
6.1 准备数据文件
# data.json 文件内容
{"index":{"_id":"1"}}
{"title":"商品1","price":1000,"tags":["tag1"],"stock":100}
{"index":{"_id":"2"}}
{"title":"商品2","price":2000,"tags":["tag2"],"stock":200}
{"index":{"_id":"3"}}
{"title":"商品3","price":3000,"tags":["tag3"],"stock":300}
6.2 使用 curl 导入
# 从文件批量导入
curl -X POST "localhost:9200/shop_product/_bulk" \
-H "Content-Type: application/json" \
--data-binary @data.json
7. 批量插入最佳实践
7.1 数据预处理
# 批量插入前先创建索引和映射
PUT /shop_product
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"title": {"type": "text", "analyzer": "standard"},
"price": {"type": "double"},
"tags": {"type": "keyword"},
"stock": {"type": "integer"}
}
}
}
7.2 分批处理大量数据
# 分批插入大量数据
POST /shop_product/_bulk
{"index":{"_id":"1"}}
{"title":"商品1","price":1000}
# ... 1000个文档
# 等待一段时间后继续下一批
POST /shop_product/_bulk
{"index":{"_id":"1001"}}
{"title":"商品1001","price":1000}
# ... 下一批1000个文档
7.3 监控批量插入进度
# 检查索引状态
GET /shop_product/_stats
# 检查文档数量
GET /shop_product/_count
# 检查索引健康状态
GET /_cluster/health/shop_product
8. 批量插入 vs 单个插入对比
| 操作方式 | 方法 | 性能 | 适用场景 |
|---|---|---|---|
| 单个插入 | POST /index/_doc |
较慢 | 少量数据,实时插入 |
| 批量插入 | POST /index/_bulk |
很快 | 大量数据,批量导入 |
| 混合操作 | POST /index/_bulk |
中等 | 需要同时插入、更新、删除 |
9. 常见问题解决
9.1 内存不足
# 减少批量大小
POST /shop_product/_bulk
# 只包含500个文档而不是1000个
9.2 超时问题
# 增加超时时间
POST /shop_product/_bulk?timeout=120s
9.3 版本冲突
# 使用 upsert 避免版本冲突
POST /shop_product/_bulk
{"update":{"_id":"1"}}
{"doc":{"price":1000},"upsert":{"title":"商品1","price":1000}}
10. 批量插入监控
# 监控批量操作性能
GET /_nodes/stats/indices/indexing
# 查看索引统计
GET /shop_product/_stats/indexing
# 监控集群状态
GET /_cluster/health?pretty
mget 批量获取操作详解
ES 中的 mget(multi-get)API 允许一次性获取多个文档,比单个获取更高效,特别适合需要批量读取数据的场景。
1. 基础批量获取
1.1 从同一索引获取多个文档
POST /shop_product/_mget
{
"docs": [
{"_id": "1"},
{"_id": "2"},
{"_id": "3"}
]
}
1.2 从不同索引获取文档
POST /_mget
{
"docs": [
{"_index": "shop_product", "_id": "1"},
{"_index": "shop_user", "_id": "100"},
{"_index": "shop_order", "_id": "200"}
]
}
1.3 指定返回字段
POST /shop_product/_mget
{
"docs": [
{
"_id": "1",
"_source": ["title", "price"]
},
{
"_id": "2",
"_source": ["title", "tags"]
}
]
}
2. 批量获取响应处理
# mget 响应示例
{
"docs": [
{
"_index": "shop_product",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"title": "iPhone 15",
"price": 5999,
"tags": ["phone", "apple"],
"stock": 100
}
},
{
"_index": "shop_product",
"_id": "2",
"_version": 1,
"_seq_no": 1,
"_primary_term": 1,
"found": true,
"_source": {
"title": "Samsung Galaxy",
"price": 4999,
"tags": ["phone", "android"],
"stock": 50
}
},
{
"_index": "shop_product",
"_id": "999",
"found": false
}
]
}
3. 高级批量获取
3.1 使用 ids 参数(简化语法)
POST /shop_product/_mget
{
"ids": ["1", "2", "3", "999"]
}
3.2 排除不需要的字段
POST /shop_product/_mget
{
"docs": [
{
"_id": "1",
"_source": {
"excludes": ["description", "created_at"]
}
},
{
"_id": "2",
"_source": {
"includes": ["title", "price"]
}
}
]
}
3.3 获取存储字段
POST /shop_product/_mget
{
"docs": [
{
"_id": "1",
"stored_fields": ["title", "price"]
}
]
}
4. 批量获取性能优化
4.1 合理控制批量大小
# 推荐的批量大小:100-1000 个文档
POST /shop_product/_mget
{
"ids": [
"1", "2", "3", "4", "5",
# ... 更多 ID
"100"
]
}
4.2 使用路由优化
POST /shop_product/_mget
{
"docs": [
{
"_id": "1",
"routing": "user123"
},
{
"_id": "2",
"routing": "user123"
}
]
}
5. 错误处理
5.1 处理不存在的文档
POST /shop_product/_mget
{
"ids": ["1", "999", "2"]
}
# 响应中包含 found: false 的文档
{
"docs": [
{
"_index": "shop_product",
"_id": "1",
"found": true,
"_source": {...}
},
{
"_index": "shop_product",
"_id": "999",
"found": false
},
{
"_index": "shop_product",
"_id": "2",
"found": true,
"_source": {...}
}
]
}
5.2 过滤不存在的文档
# 只返回找到的文档
POST /shop_product/_mget?filter_path=docs._source
{
"ids": ["1", "999", "2"]
}
6. 实际应用场景
6.1 购物车商品信息获取
# 根据购物车中的商品 ID 批量获取商品信息
POST /shop_product/_mget
{
"ids": ["cart_item_1", "cart_item_2", "cart_item_3"]
}
6.2 用户订单详情获取
# 批量获取用户的所有订单详情
POST /shop_order/_mget
{
"docs": [
{"_id": "order_001", "_source": ["order_id", "total", "status"]},
{"_id": "order_002", "_source": ["order_id", "total", "status"]},
{"_id": "order_003", "_source": ["order_id", "total", "status"]}
]
}
6.3 跨索引数据关联
# 同时获取用户信息和用户订单
POST /_mget
{
"docs": [
{"_index": "shop_user", "_id": "user_123", "_source": ["name", "email"]},
{"_index": "shop_order", "_id": "order_456", "_source": ["order_id", "total"]},
{"_index": "shop_product", "_id": "product_789", "_source": ["title", "price"]}
]
}
7. mget vs 单个获取对比
| 操作方式 | 方法 | 性能 | 适用场景 |
|---|---|---|---|
| 单个获取 | GET /index/_doc/id |
较慢 | 获取单个文档 |
| 批量获取 | POST /index/_mget |
很快 | 批量获取多个文档 |
| 搜索获取 | POST /index/_search |
中等 | 根据条件获取文档 |
8. 批量获取最佳实践
8.1 数据预处理
# 批量获取前先检查索引状态
GET /shop_product/_stats
# 检查文档是否存在
GET /shop_product/_count
8.2 分批处理大量数据
# 分批获取大量文档
POST /shop_product/_mget
{
"ids": ["1", "2", "3", "4", "5"]
# 第一批 100 个文档
}
# 继续下一批
POST /shop_product/_mget
{
"ids": ["6", "7", "8", "9", "10"]
# 下一批 100 个文档
}
8.3 缓存策略
# 使用缓存提高性能
POST /shop_product/_mget?preference=_local
{
"ids": ["1", "2", "3"]
}
9. 常见问题解决
9.1 内存不足
# 减少批量大小
POST /shop_product/_mget
{
"ids": ["1", "2", "3"] # 只获取 3 个文档
}
9.2 超时问题
# 增加超时时间
POST /shop_product/_mget?timeout=60s
{
"ids": ["1", "2", "3"]
}
9.3 索引不存在
# 处理索引不存在的情况
POST /_mget
{
"docs": [
{"_index": "shop_product", "_id": "1"},
{"_index": "non_existent_index", "_id": "2"}
]
}
10. 批量获取监控
# 监控批量获取性能
GET /_nodes/stats/indices/search
# 查看索引搜索统计
GET /shop_product/_stats/search
# 监控集群状态
GET /_cluster/health?pretty
11. 与其他批量操作结合
11.1 批量获取 + 批量更新
# 先批量获取
POST /shop_product/_mget
{
"ids": ["1", "2", "3"]
}
# 然后批量更新
POST /shop_product/_bulk
{"update":{"_id":"1"}}
{"doc":{"stock":95}}
{"update":{"_id":"2"}}
{"doc":{"stock":45}}
{"update":{"_id":"3"}}
{"doc":{"stock":15}}
11.2 批量获取 + 搜索
# 先搜索获取相关文档 ID
POST /shop_product/_search
{
"query": {"match": {"tags": "phone"}},
"_source": false,
"size": 10
}
# 然后批量获取详细信息
POST /shop_product/_mget
{
"ids": ["1", "2", "3", "4", "5"]
}
ES 的 query:{} 功能详解
ES 中的 query 是搜索请求的核心部分,用于定义搜索条件和逻辑。query:{} 是一个空的查询对象,通常与 match_all 结合使用。
1. query 对象基础结构
POST /shop_product/_search
{
"query": {
// 查询条件定义在这里
}
}
2. 常用查询类型
2.1 match_all 查询(全表扫描)
POST /shop_product/_search
{
"query": {
"match_all": {}
}
}
功能说明:
- 匹配索引中的所有文档
- 相当于 SQL 中的
SELECT * FROM table - 常用于获取所有数据或作为基础查询
2.2 match 查询(全文搜索)
POST /shop_product/_search
{
"query": {
"match": {
"title": "iPhone"
}
}
}
功能说明:
- 对指定字段进行全文搜索
- 会对查询词进行分词处理
- 支持模糊匹配和相关性评分
2.3 term 查询(精确匹配)
POST /shop_product/_search
{
"query": {
"term": {
"tags": "phone"
}
}
}
功能说明:
- 精确匹配,不进行分词
- 适用于 keyword 类型字段
- 性能比 match 查询更好
2.4 range 查询(范围查询)
POST /shop_product/_search
{
"query": {
"range": {
"price": {
"gte": 1000,
"lte": 5000
}
}
}
}
功能说明:
- 对数值、日期等字段进行范围查询
- 支持
gte(大于等于)、gt(大于)、lte(小于等于)、lt(小于) - 常用于价格区间、时间范围等查询
2.5 bool 查询(复合查询)
POST /shop_product/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "phone"}},
{"range": {"price": {"gte": 1000}}}
],
"must_not": [
{"term": {"status": "discontinued"}}
],
"should": [
{"match": {"tags": "apple"}}
],
"filter": [
{"term": {"category": "electronics"}}
]
}
}
}
功能说明:
must:必须匹配,影响相关性评分must_not:必须不匹配,不影响评分should:应该匹配,提高相关性评分filter:必须匹配,但不影响评分,性能更好
2.6 wildcard 查询(通配符查询)
POST /shop_product/_search
{
"query": {
"wildcard": {
"title": {
"value": "iPh*"
}
}
}
}
功能说明:
- 支持
*(匹配任意字符)和?(匹配单个字符) - 性能较慢,不推荐在大量数据上使用
- 适用于模糊匹配场景
2.7 prefix 查询(前缀查询)
POST /shop_product/_search
{
"query": {
"prefix": {
"title": "iPh"
}
}
}
功能说明:
- 匹配以指定前缀开头的文档
- 适用于自动补全、搜索建议等场景
- 性能比 wildcard 查询更好
2.8 fuzzy 查询(模糊查询)
POST /shop_product/_search
{
"query": {
"fuzzy": {
"title": {
"value": "iphone",
"fuzziness": "AUTO"
}
}
}
}
功能说明:
- 支持拼写错误的容错查询
fuzziness参数控制容错程度- 适用于搜索纠错场景
3. 查询性能优化
3.1 使用 filter 而非 query
POST /shop_product/_search
{
"query": {
"bool": {
"filter": [
{"range": {"price": {"gte": 1000}}}
]
}
}
}
优势:
- filter 不计算相关性评分,性能更好
- 结果会被缓存,重复查询更快
- 适用于精确匹配条件
3.2 合理使用 _source 过滤
POST /shop_product/_search
{
"_source": ["title", "price"],
"query": {
"match_all": {}
}
}
优势:
- 只返回需要的字段,减少网络传输
- 提高查询性能
- 降低内存使用
3.3 使用 size 控制返回数量
POST /shop_product/_search
{
"size": 10,
"query": {
"match_all": {}
}
}
优势:
- 避免一次性返回大量数据
- 提高查询响应速度
- 减少内存消耗
4. 查询调试技巧
4.1 使用 explain 参数
POST /shop_product/_search
{
"explain": true,
"query": {
"match": {
"title": "iPhone"
}
}
}
功能:
- 显示每个文档的评分计算过程
- 帮助理解查询结果排序原因
- 用于查询优化和调试
4.2 使用 profile 参数
POST /shop_product/_search
{
"profile": true,
"query": {
"bool": {
"must": [
{"match": {"title": "phone"}},
{"range": {"price": {"gte": 1000}}}
]
}
}
}
功能:
- 显示查询执行的详细时间信息
- 帮助识别性能瓶颈
- 用于查询性能优化
5. 查询最佳实践
5.1 查询结构优化
# 好的查询结构
POST /shop_product/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "phone"}}
],
"filter": [
{"range": {"price": {"gte": 1000, "lte": 5000}}},
{"term": {"status": "active"}}
]
}
},
"sort": [{"price": "desc"}],
"size": 20
}
5.2 避免深度分页
# 使用 search_after 替代 from/size
POST /shop_product/_search
{
"query": {"match_all": {}},
"size": 100,
"sort": [{"_id": "asc"}],
"search_after": ["last_doc_id"]
}
5.3 合理使用聚合
POST /shop_product/_search
{
"size": 0,
"aggs": {
"price_stats": {
"stats": {"field": "price"}
},
"tags_count": {
"terms": {"field": "tags", "size": 10}
}
}
}
6. 常见查询模式
6.1 搜索 + 过滤
POST /shop_product/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "用户搜索词"}}
],
"filter": [
{"term": {"category": "electronics"}},
{"range": {"price": {"gte": 1000}}}
]
}
}
}
6.2 多字段搜索
POST /shop_product/_search
{
"query": {
"multi_match": {
"query": "iPhone",
"fields": ["title^2", "description", "tags"]
}
}
}
6.3 嵌套对象查询
POST /shop_product/_search
{
"query": {
"nested": {
"path": "reviews",
"query": {
"bool": {
"must": [
{"match": {"reviews.comment": "good"}},
{"range": {"reviews.rating": {"gte": 4}}}
]
}
}
}
}
}
7. 查询性能监控
# 监控查询性能
GET /_nodes/stats/indices/search
# 查看慢查询日志
GET /_nodes/stats/indices/search?filter_path=*.search.query_time_in_millis
# 监控集群查询状态
GET /_cluster/health?pretty
8. 查询错误处理
8.1 处理查询语法错误
# 错误的查询
POST /shop_product/_search
{
"query": {
"match": {
"title": "iPhone"
// 缺少闭合括号
}
}
}
# 错误响应
{
"error": {
"type": "parsing_exception",
"reason": "Unexpected end-of-input"
}
}
8.2 处理字段不存在错误
# 查询不存在的字段
POST /shop_product/_search
{
"query": {
"match": {
"non_existent_field": "value"
}
}
}
# 响应(不会报错,但不会匹配任何文档)
{
"hits": {
"total": {"value": 0, "relation": "eq"},
"hits": []
}
}
9. 查询缓存策略
# 使用查询缓存
POST /shop_product/_search
{
"query": {
"bool": {
"filter": [
{"term": {"category": "electronics"}}
]
}
}
}
# 缓存会被自动使用,提高重复查询性能
10. 总结
ES 的 query:{} 功能是搜索的核心,掌握各种查询类型和优化技巧对于构建高性能的搜索应用至关重要:
- 基础查询:match_all、match、term、range
- 复合查询:bool 查询组合多个条件
- 性能优化:合理使用 filter、_source 过滤、size 控制
- 调试技巧:explain 和 profile 参数
- 最佳实践:避免深度分页、合理使用聚合、监控性能
通过合理使用这些查询功能,可以构建出高效、准确的搜索系统。
主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://joyjs.cn/archives/4783