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://walker-learn.xyz/archives/6757

(0)
Walker的頭像Walker
上一篇 6小時前
下一篇 8小時前

相關推薦

  • 編程基礎 0007_併發模式

    Go 併發模式 常見的 Go 併發設計模式,每個模式都有完整可運行示例和適用場景說明 1. Worker Pool 模式 固定數量的 worker goroutine 從共享的任務隊列中取任務執行,控制併發度。 package main import ( "fmt" "sync" "time" ) …

    後端開發 19小時前
    400
  • Go工程師體系課 007

    商品微服務 實體結構說明 本模塊包含以下核心實體: 商品(Goods) 商品分類(Category) 品牌(Brands) 輪播圖(Banner) 品牌分類(GoodsCategoryBrand) 1. 商品(Goods) 描述平臺中實際展示和銷售的商品信息。 字段說明 字段名 類型 說明 name String 商品名稱,必填 brand Pointer …

    後端開發 10小時前
    100
  • Go工程師體系課 004

    需求分析 後臺管理系統 商品管理 商品列表 商品分類 品牌管理 品牌分類 訂單管理 訂單列表 用戶信息管理 用戶列表 用戶地址 用戶留言 輪播圖管理 電商系統 登錄頁面 首頁 商品搜索 商品分類導航 輪播圖展示 推薦商品展示 商品詳情頁 商品圖片展示 商品描述 商品規格選擇 加入購物車 購物車 商品列表 數量調整 刪除商品 結算功能 用戶中心 訂單中心 我的…

    13小時前
    100
  • 編程基礎 0009_testing詳解

    Go testing 詳解 目錄 testing 包基礎 表格驅動測試 子測試 t.Run 基準測試 Benchmark 測試覆蓋率 TestMain httptest 包 Mock 和接口測試技巧 模糊測試 Fuzz 1. testing 包基礎 1.1 測試文件和函數命名規則 Go 測試遵循嚴格的命名約定: 測試文件以 _test.go 結尾(如 use…

    後端開發 20小時前
    300
  • Go工程師體系課 003

    grpc grpc grpc-go grpc 無縫集成了 protobuf protobuf 習慣用 Json、XML 數據存儲格式的你們,相信大多都沒聽過 Protocol Buffer。 Protocol Buffer 其實是 Google 出品的一種輕量 & 高效的結構化數據存儲格式,性能比 Json、XML 真的強!太!多! protobuf…

簡體中文 繁體中文 English