該文章同步發佈於:我的部落格
也歡迎關注我的 Facebook 以及 Instagram 接收軟體相關的資訊!
上一篇文章,我們學會了使用 script
的方式來更新 document 以及根據條件式返回我們需要的 result
。
這邊快速地帶過如何 upsert
一個 document。
對於工程師來說,這個字詞並不陌生,其實就是 update
+ insert
的組合詞,白話文來說:
當一個資料存在時,更新它,不存在時,寫入它
在 Elasticsearch ( 以下簡稱 ES ) 如何做到呢?很簡單。
加上 upsert
的 key 並且帶上基礎的 payload,而上方的 script
則是當資料存在時,執行的更新策略。
所以當我們第一次執行時,會建立一個叫做 Ruffy
的 user,而第二次執行時,會基於 age
的值再加 1,可以自己試試看。
注意我的 endpoint 是指向
/user/_update/2
這個_id
2 一開始是不存在的,所以才有upsert
的效果。
刪除的部分,就是使用 HTTP Verb 的 DELETE
就可以做到了,DELETE /user/_doc/:id
取代是什麼意思呢?這個在前幾篇文章就有提過,其實 document 本身是不可變的,每一次的更新都可以算作是一種取代,而使用 PUT /user/_doc/:id
的方式,就可以取代某一資源。
那取代和更新有什麼不同呢?更新的概念在於局部更新,例如,前面的範例都是根據 age
這個值做更新,但取代的概念是,當我們 PUT
時沒有 age
這個值,而新的 document 也不會有 age
這個值。
可以自己在 Dev tools 上面試試看,蠻直覺的。
還記得嗎?我們是處在一個具有 3 個 shards 的叢集之中,問題來了?ES 怎麼知道我們找的 document 在哪一個 shard 呢?
答案就是 routing
,具體 routing 是如何運作的呢?
下面是 routing 如何找到存放 document 的 shard 的公式:
shard_num = hash(_routing) % num_primary_shard
想像一個請求進來,ES 就根據公式指出了存放 document 的 shard,如下圖所示:
在 ES 中,這個 routing 的策略是可以做客製化的,但內容會比想像中的複雜,如果有機會的話會提到,但目前我們使用的就是 預設的 Routing 策略。
而預設的 routing 策略有什麼好處呢?可以讓 ES 平均的分配 document 到現有的 shard 之中。
當我們客製化了公式,那代表我們需要注意的地方更多,平均分配就會是很重要的一個環節。
下方是一個正常的 document 結構:
{
"_index": "users",
"_id": "1",
"_version": 15,
"_seq_no": 15,
"_primary_term": 1,
"found": true,
"_source": {
"name": "John",
"age": 20,
"gender": "male",
"habits": [
"baseball"
]
}
}
當使用 ES 預設的 routing 策略時,ES 會使用 _id
作為 routing ( 那個放到公式內的 _routing )
而當客製化後,就會出現 _routing
的值。
還記得在建立一個 index 的時候,我們需要在一開始就決定 primary shard 的數量。
這就和這個公式有關了,當我們隨意的新增 primary shard 時,等於是打壞了公式,會讓 ES 找不到 document 存放的正確位置,也會把整個規則都打亂。
這也是為什麼我們會需要 split
以及 shrink
API 來幫助我們做這些事情,當新增 primary shard 時,所有的 documents 都需要再重新地 index 一次。
就是為了讓所有的 shards 都對應到公式中 num_primary_shard
新的值。
試想一下,可以隨意地增加或減少 primary shard,這個公式會變成一個災難,shard 找不到,document 也更新不了。
會有這樣的疑慮是很正常的,我們可以把 primary shard 以及它的 replica shard 視作一個 replication group 還記得嗎?
所以當 ES 找到指定的 shard 時,可以視作找到了 replication group,而這時候透過一個叫做 ARS 的算法找到最適合的 shard 來回應資料,這才是真正的達到了擴展的目的,我們不希望每一次的請求都落在 primary shard 身上。
什麼是 ARS? Adaptive replica selection,不需要知道細節,只要知道它會嘗試找到效能最好的路徑,並指定那個 shard 來返回結果。