Website logo

Robert Chang

技術部落格

Elasticsearch - Bulk Import

我們現在已經學會怎麼 index, update, delete document 了。

今天要介紹的是 bulk API,可以把所有的動作濃縮到一個請求的 payload 之中,是一個非常實用的 API。

首先,這個 API 需要的 payload 格式是 NDJSON,全名:Newline Delimited JSON,是一個把多個 JSON 物件串接在一起的格式。

看起來會像這樣:

{ "name": "Alice", "age": 25 }
{ "name": "Bob", "age": 30 }
{ "name": "Charlie", "age": 35 }

實際上每一行的結尾都包含 \n 的換行符號,只是如果用編輯器撰寫的話,直接按 Enter 就可以了。

使用 cURL 之類的工具時,HTTP Header 的 Content-Type 記得放 application/x-ndjson

一定要用 application/x-ndjson 嗎?

不,其實你用 application/json 也是可以被接受的,但就不是正確的方式。

實際操作

直接進到 Dev Tools 內,指向 POST /_bulk 的 API:

screen shot

我總共填入了 4 行的 JSON 物件,一行行來解析一下。

第一行,我使用了 index 這個動作以及 _id 這樣的 metadata,第二行則是第一行這個動作要處理的內容。

所以第一行加上第二行的意思就是,我要 index 一個選手,名字叫做 Ohtani, Power 99, Speed 80,第三行加上第四行也是以此類推。

等等,那 indexcreate 差在哪?

create 如果目標的 _id 已經存在,就會出現錯誤,而 index 則會取代原本存在的 document。

同樣的請求,我們再送一次看看:

screen shot

看到了嗎?錯誤的原因:

[2]: version conflict, document already exists (current version [1])

而原先第一次的 created 也變成 updated 了,這是因為第一個動作是採用 index 的緣故。

接著我們來試試看 update 以及 delete 的操作:

screen shot

有注意到什麼不一樣的地方嗎?

首先,我們把 endpoint 換成 /players/_bulk,當我們確定 payload 中的所有資料都是在針對同一個 index 操作時,就可以直接在 endpoint 中指定,也就可以少寫 “_index”: “player” 的值。

第二個,update 的動作中,要使用 doc 當作 key 讓 Elasticsearch ( 以下簡稱 ES ) 知道我們要更新 document 內的某個欄位;還記得老朋友 script 嗎?這邊也可以使用,忘記的話去看 這篇

至於 delete 就沒什麼好說的,不需要額外提供資訊,刪掉就可以了。

如果其中發生錯誤的話?

上一篇文章update_by_query 的方式一樣,每一個動作本身並不會影響到其他的動作,也就是說其中一個發生錯誤了,其他的還是會繼續執行,直到最後 ES 會把所有更新的細節回傳給你,接著你再來決定要怎麼做。

而從剛剛的截圖中也能看到 items 裡面包含的物件排序和我們送出請求時是一樣的,這樣使用應用程式處理時,也很方便。

什麼時候用?好處是什麼?

其實從作用就很容易地看出來,主要是在處理大量資料時使用,那什麼時候會需要這樣處理大量資料呢?

應用場景:

  1. 資料的初始化,或是資料遷移的時候。
  2. Elastic 全家桶 這篇文章中有提到 Logstash 這樣的工具,在導入資料進入 ES 時,使用 _bulk API 也是很常見的做法。
  3. 定期同步資料,我們時常會為了加速搜尋體驗,把關聯式資料庫的資料 index 到 ES 之中,這時候用 _bulk API 就能大幅度地提升 index 或是 update 的速度。

好處:

  1. 效率,光從 1000 個請求降低到 1 個請求,這中間省下多少網路傳輸的時間呢?
  2. 資源最優化,這和上面是相關的,每次的請求都會有 建立連接、I/O 操作、處理、回應 等等的基本步驟,而 _bulk 讓這些事情集中到一起,一次處理,減少很多 ES 自身的消耗,當然關於記憶體的利用也會比單獨更新更好。
  3. 減少 Cold Start 的時間,一些套件和資料結構在收到第一個請求時可能需要「 暖機 」,之後的請求可以利用這些已經「 暖機 」的套件,減少等待時間。
  4. 方便 Debug,就像前面說的,錯誤的訊息也會隨著 response 一起回來,所以我們可以快速地針對這些錯誤處理。

咦,Bulk API 不支援鎖嗎?

前兩篇文章都不停地提到 ES 使用 primary term 以及 sequence number 來實作樂觀鎖的機制,那 _bulk API 呢?支援嗎?

當然,你只要把我們在 這篇文章 中學過的 if_primary_term 以及 if_seq_no 帶入第一行動作的 metadata 就可以囉!

隨手範例:

{ "index": { "_index": "players", "_id": 1, "if_primary_term": 10, "if_seq_no": 4 } }
{ "name": "Ohtani", "power": 99, "speed": 75 }

上一篇文章Elasticsearch - Update & Delete by query

下一篇文章Elasticsearch - Analyzer 介紹