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

相关推荐

  • Go工程师体系课 protoc-gen-validate【学习笔记】

    protoc-gen-validate 简介与使用指南 ✅ 什么是 protoc-gen-validate protoc-gen-validate(简称 PGV)是一个 Protocol Buffers 插件,用于在生成的 Go 代码中添加结构体字段的验证逻辑。 它通过在 .proto 文件中添加 validate 规则,自动为每个字段生成验证代码,避免你手…

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

    代理(Proxy)和反射(Reflection)API 代理是一种可以拦截并改变底层javascript引擎操作的包装器,在新语言中通过它暴露内部运作对象,从而让开发者可以创建内建的对象。 代理陷阱 覆写的特性 默认特性 get 读取一个属性值 Reflect.get() set 写入一个属性值 Reflect.set() has in操作符 Reflect…

    个人 2025年3月8日
    96900
  • 深入理解ES6 006【学习笔记】

    Symbol和Symbol属性 第6种原始数据类型:Symbol。私有名称原本是为了让开发者们创建非字符串属性名称而设计的,但是一般的技术无法检测这些属性的私有名称 创建Symbol let firstName = Symbol(); let person = {} person[firstName] = "Nicholas"; cons…

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

    订单事务 先扣库存 后扣库存 都会对库存和订单都会有影响, 所以要使用分布式事务 业务(下单不对付)业务问题 支付成功再扣减(下单了,支付时没库存了) 订单扣减,不支付(订单超时归还)【常用方式】 事务和分布式事务 1. 什么是事务? 事务(Transaction)是数据库管理系统中的一个重要概念,它是一组数据库操作的集合,这些操作要么全部成功执行,要么全部…

    个人 2025年11月25日
    4200
  • 热爱运动,挑战极限,拥抱自然

    热爱 在这个快节奏的时代,我们被工作、生活的压力所包围,常常忽略了身体的需求。而运动,不仅仅是一种健身方式,更是一种释放自我、挑战极限、与自然共舞的生活态度。无论是滑雪、攀岩、冲浪,还是跑步、骑行、瑜伽,每一种运动都能让我们找到内心的激情,感受到生命的跃动。 运动是一场自我挑战 挑战极限,不仅仅是职业运动员的专属,而是每一个热爱运动的人都可以追求的目标。它可…

    个人 2025年2月26日
    1.1K00

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

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

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