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/4783

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

相關推薦

  • TS珠峰 001【學習筆記】

    課程大綱 搭建 TypeScript 開發環境。 掌握 TypeScript 的基礎類型,聯合類型和交叉類型。 詳細類型斷言的作用和用法。 掌握 TypeScript 中函數、類的類型聲明方式。 掌握類型別名、接口的作用和定義。 掌握泛型的應用場景,熟練應用泛型。 靈活運用條件類型、映射類型與內置類型。 創建和使用自定義類型。 理解命名空間、模塊的概念已經使…

    個人 2025年3月27日
    1.5K00
  • 深入理解ES6 006【學習筆記】

    Symbol和Symbol屬性 第6種原始數據類型:Symbol。私有名稱原本是為了讓開發者們創建非字符串屬性名稱而設計的,但是一般的技術無法檢測這些屬性的私有名稱 創建Symbol let firstName = Symbol(); let person = {} person[firstName] = "Nicholas"; cons…

    個人 2025年3月8日
    1.2K00
  • TS珠峰 004【學習筆記】

    類型體操 type-1 // 內置 // Partial Required Readonly 修飾類型的 // Pick Omit 處理數據結構 // Exclude Extract 處理集合類型的 // Paramters RetrunValue infer // 字符串類型,模板字符串`${}` + infer PartialPropsOptional …

    個人 2025年3月27日
    1.4K00
  • 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日
    21000
  • 【開篇】

    我是Walker,生於八十年代初,代碼與生活的旅者。全棧開發工程師,游走於前端與後端的邊界,執著於技術與藝術的交匯點。 代碼,是我編織夢想的語言;項目,是我刻畫未來的畫布。在鍵盤的敲擊聲中,我探索技術的無盡可能,讓靈感在代碼里永恆綻放。 深度咖啡愛好者,迷戀每一杯手衝的詩意與儀式感。在咖啡的醇香與苦澀中,尋找專注與靈感,亦如在開發的世界中追求極致與平衡。 騎…

    2025年2月6日 個人
    2.2K00
簡體中文 繁體中文 English
歡迎🌹 Coding never stops, keep learning! 💡💻 光臨🌹