Go工程师体系课 010【学习笔记】

Table of Contents

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 等专用类型。

常见操作对照

  1. 创建索引(含映射)

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"}
    }
  }
}
  1. 写入一行/文档

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"
}
  1. 按主键查询

SQL:

SELECT * FROM product WHERE id=1;

ES:

GET /shop_product/_doc/1
  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}}} ]
    }
  }
}
  1. 更新与删除

SQL:UPDATE ... WHERE id=? / DELETE FROM ... WHERE id=?

ES:

POST /shop_product/_update/1
{"doc": {"price": 5799}}

DELETE /shop_product/_doc/1
  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 支持两种查询方式:

  1. URL 参数查询GET _search?q=field:value(简单快速)
  2. Request Body 查询POST _search + JSON body(功能强大,推荐)

为什么推荐 Request Body 查询?

  • 功能完整:支持复杂查询、聚合、排序、分页等
  • 可读性强:JSON 结构清晰,便于维护
  • 性能更好:避免 URL 长度限制,支持更复杂的查询逻辑
  • 调试友好:Kibana Dev Tools 中直接粘贴执行

基础查询示例

  1. 简单匹配查询
POST /shop_product/_search
{
  "query": {
    "match": {
      "title": "iPhone"
    }
  }
}
  1. 多条件组合查询
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"}}
      ]
    }
  }
}
  1. 精确匹配 vs 全文搜索
# 精确匹配(不分词)
POST /shop_product/_search
{
  "query": {
    "term": {
      "tags": "phone"
    }
  }
}

# 全文搜索(会分词)
POST /shop_product/_search
{
  "query": {
    "match": {
      "title": "iPhone 15 Pro"
    }
  }
}
  1. 分页与排序
POST /shop_product/_search
{
  "from": 0,
  "size": 10,
  "sort": [
    {"price": {"order": "desc"}},
    {"_score": {"order": "desc"}}
  ],
  "query": {
    "match_all": {}
  }
}
  1. 指定返回字段
POST /shop_product/_search
{
  "_source": ["title", "price", "tags"],
  "query": {
    "match_all": {}
  }
}
  1. 聚合查询(统计)
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}
        ]
      }
    }
  }
}
  1. 高亮显示
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"}}}}

性能优化建议

  1. 使用 filter 而非 query:filter 不计算相关性分数,性能更好
POST /shop_product/_search
{
  "query": {
    "bool": {
      "filter": [
        {"range": {"price": {"gte": 1000}}}
      ]
    }
  }
}
  1. 合理使用 size:避免一次性返回大量数据
  2. 使用 _source 过滤:只返回需要的字段
  3. 缓存常用查询: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. 性能注意事项

  1. 部分更新性能更好:只传输变更的字段,减少网络开销
  2. 脚本更新较慢:需要解析和执行脚本,性能相对较低
  3. 批量操作:大量更新时使用 _bulk API 提高效率
  4. 版本控制: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 删除的数据无法直接恢复,但可以通过以下方式:

  1. 从快照恢复:如果之前创建了快照
  2. 从备份恢复:如果有数据备份
  3. 重新导入:从原始数据源重新导入
# 从快照恢复索引
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

(0)
Walker的头像Walker
上一篇 2025年11月25日 10:00
下一篇 2025年11月25日 07:00

相关推荐

  • 深入理解ES6 008【学习笔记】

    迭代器(Iterator)和生成器(Generator) 这个新特性对于高效的数据处理而言是不可或缺的,你也会发现在语言的其他特性中也都有迭代器的身影:新的for-of循环、展开运算符(...)、甚至连异步编程都可以使用迭代器。 迭代器是一种特殊的对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对…

    个人 2025年3月8日
    1.1K00
  • Go工程师体系课 012【学习笔记】

    Go 中集成 Elasticsearch 1. 客户端库选择 1.1 主流 Go ES 客户端 olivere/elastic:功能最全面,API 设计优雅,支持 ES 7.x/8.x elastic/go-elasticsearch:官方客户端,轻量级,更接近原生 REST API go-elasticsearch/elasticsearch:社区维护的官…

    个人 2025年11月25日
    18200
  • 深入理解ES6 004【学习笔记】

    扩展对象的功能 普通对象 具有js对象所有默认内部行为的 特异对象 具有某些与默认行为不符的内部行为 标准对象 es6规范中定义的对象,Array/Date等 内建对象 脚本开始执行时存在于javascript执行环境的对象,所有标准对象都是内建对象 对象字面量语法扩展 属性初始值的简写,当一个对象的属性与本地变量同名时,不必再写冒号和值 对象方法的简写语法…

    个人 2025年3月8日
    1.2K00
  • Go工程师体系课 004【学习笔记】

    需求分析 后台管理系统 商品管理 商品列表 商品分类 品牌管理 品牌分类 订单管理 订单列表 用户信息管理 用户列表 用户地址 用户留言 轮播图管理 电商系统 登录页面 首页 商品搜索 商品分类导航 轮播图展示 推荐商品展示 商品详情页 商品图片展示 商品描述 商品规格选择 加入购物车 购物车 商品列表 数量调整 删除商品 结算功能 用户中心 订单中心 我的…

    个人 2025年11月25日
    17800
  • Go工程师体系课 009【学习笔记】

    其它一些功能 个人中心 收藏 管理收货地址(增删改查) 留言 拷贝inventory_srv--> userop_srv 查询替换所有的inventory Elasticsearch 深度解析文档 1. 什么是Elasticsearch Elasticsearch是一个基于Apache Lucene构建的分布式、RESTful搜索和分析引擎,能够快速地…

    个人 2025年11月25日
    19400

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
欢迎🌹 Coding never stops, keep learning! 💡💻 光临🌹