Go工程師體系課 010

18次閱讀

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 參數
  • 最佳實踐:避免深度分頁、合理使用聚合、監控性能

通過合理使用這些查詢功能,可以構建出高效、準確的搜索系統。

正文完
 0