Elasticsearch批量操作bulk原理解釋

代價較小的批量操作

與 mget 可以使我們一次取回多個文檔同樣的方式, bulk API 允許在單個步驟中進行多次 create 、 index 、 update 或 delete 請求。 如果你需要索引一個數據流比如日誌事件,它可以排隊和索引數百或數千批次。

bulk 與其他的請求體格式稍有不同,如下所示:

{ action: { metadata }}\n
{ request body        }\n
{ action: { metadata }}\n
{ request body        }\n
...

這種格式類似一個有效的單行 JSON 文檔  ,它通過換行符(\n)連接到一起。注意兩個要點:

  • 每行一定要以換行符(\n)結尾, 包括最後一行 。這些換行符被用作一個標記,可以有效分隔行。
  • 這些行不能包含未轉義的換行符,因爲他們將會對解析造成干擾。這意味着這個 JSON  能使用 pretty 參數打印。

在 爲什麼是有趣的格式? 中, 我們解釋爲什麼 bulk API 使用這種格式。

action/metadata 行指定 哪一個文檔 做 什麼操作 。

action 必須是以下選項之一:

create

如果文檔不存在,那麼就創建它。

index

創建一個新文檔或者替換一個現有的文檔。

update

部分更新一個文檔。

delete

刪除一個文檔。

metadata 應該 指定被索引、創建、更新或者刪除的文檔的 _index 、 _type 和 _id 。

例如,一個 delete 請求看起來是這樣的:

{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}

request body 行由文檔的 _source 本身組成--文檔包含的字段和值。它是 index 和 create 操作所必需的,這是有道理的:你必須提供文檔以索引。

它也是 update 操作所必需的,並且應該包含你傳遞給 update API 的相同請求體: doc 、 upsert 、 script 等等。 刪除操作不需要 request body 行。

{ "create":  { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "My first blog post" }

如果不指定 _id ,將會自動生成一個 ID :

{ "index": { "_index": "website", "_type": "blog" }}
{ "title":    "My second blog post" }

爲了把所有的操作組合在一起,一個完整的 bulk 請求 有以下形式:

POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }} 
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "My first blog post" }
{ "index":  { "_index": "website", "_type": "blog" }}
{ "title":    "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
{ "doc" : {"title" : "My updated blog post"} } 

拷貝爲 CURL在 SENSE 中查看 

請注意 delete 動作不能有請求體,它後面跟着的是另外一個操作。

謹記最後一個換行符不要落下。

這個 Elasticsearch 響應包含 items 數組, 這個數組的內容是以請求的順序列出來的每個請求的結果。

{
   "took": 4,
   "errors": false, 
   "items": [
      {  "delete": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "_version": 2,
            "status":   200,
            "found":    true
      }},
      {  "create": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "_version": 3,
            "status":   201
      }},
      {  "create": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "EiwfApScQiiy7TIKFxRCTw",
            "_version": 1,
            "status":   201
      }},
      {  "update": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "_version": 4,
            "status":   200
      }}
   ]
}

拷貝爲 CURL在 SENSE 中查看 

所有的子請求都成功完成。

每個子請求都是獨立執行,因此某個子請求的失敗不會對其他子請求的成功與否造成影響。 如果其中任何子請求失敗,最頂層的 error 標誌被設置爲 true ,並且在相應的請求報告出錯誤明細:

POST /_bulk
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "Cannot create - it already exists" }
{ "index":  { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "But we can update it" }

拷貝爲 CURL在 SENSE 中查看 

在響應中,我們看到 create 文檔 123 失敗,因爲它已經存在。但是隨後的 index 請求,也是對文檔 123操作,就成功了:

{
   "took": 3,
   "errors": true, 
   "items": [
      {  "create": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "status":   409, 
            "error":    "DocumentAlreadyExistsException 
                        [[website][4] [blog][123]:
                        document already exists]"
      }},
      {  "index": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "_version": 5,
            "status":   200 
      }}
   ]
}

一個或者多個請求失敗。

這個請求的HTTP狀態碼報告爲 409 CONFLICT 。

解釋爲什麼請求失敗的錯誤信息。

第二個請求成功,返回 HTTP 狀態碼 200 OK 。

這也意味着 bulk 請求不是原子的: 不能用它來實現事務控制。每個請求是單獨處理的,因此一個請求的成功或失敗不會影響其他的請求。

不要重複指定Index和Type

也許你正在批量索引日誌數據到相同的 index 和 type 中。 但爲每一個文檔指定相同的元數據是一種浪費。相反,可以像 mget API 一樣,在 bulk 請求的 URL 中接收默認的 /_index 或者 /_index/_type :

POST /website/_bulk
{ "index": { "_type": "log" }}
{ "event": "User logged in" }

你仍然可以覆蓋元數據行中的 _index 和 _type , 但是它將使用 URL 中的這些元數據值作爲默認值:

POST /website/log/_bulk
{ "index": {}}
{ "event": "User logged in" }
{ "index": { "_type": "blog" }}
{ "title": "Overriding the default type" }

多大是太大了?

整個批量請求都需要由接收到請求的節點加載到內存中,因此該請求越大,其他請求所能獲得的內存就越少。 批量請求的大小有一個最佳值,大於這個值,性能將不再提升,甚至會下降。 但是最佳值不是一個固定的值。它完全取決於硬件、文檔的大小和複雜度、索引和搜索的負載的整體情況。

幸運的是,很容易找到這個 最佳點 :通過批量索引典型文檔,並不斷增加批量大小進行嘗試。 當性能開始下降,那麼你的批量大小就太大了。一個好的辦法是開始時將 1,000 到 5,000 個文檔作爲一個批次, 如果你的文檔非常大,那麼就減少批量的文檔個數。

密切關注你的批量請求的物理大小往往非常有用,一千個 1KB 的文檔是完全不同於一千個 1MB 文檔所佔的物理大小。 一個好的批量大小在開始處理後所佔用的物理大小約爲 5-15 MB。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章