問題:當我們要在 ES 中存儲數據的時候,數據應該存儲在主分片和復制分片中的哪一個中去;當我們在 ES 中檢索數據的時候,又是怎么判斷要查詢的數據是屬于哪一個分片。
?
- 數據存儲到分片的過程是一定規則的,并不是隨機發生的。
?
- 規則: shard?=?hash(routing)?%?number_of_primary_shards
?
- Routing 值可以是一個任意的字符串,默認情況下,它的值為存數數據對應文檔? _id? 值,也可以是用戶自定義的值。 Routing 這個字符串通過一個 hash 的函數處理,并返回一個數值,然后再除以索引中主分片的數目,所得的余數作為主分片的編號,取值一般在 0 到 number_of_primary_shards?-?1 的這個范圍中。通過這種方法計算出該數據是存儲到哪個分片中。
?
- 正是這種路由機制,導致了主分片的個數為什么在索引建立之后不能修改。對已有索引主分片數目的修改直接會導致路由規則出現嚴重問題,部分數據將無法被檢索。
?
2 、主分片與復制分片如何交互
為了說明這個問題,我用一個例子來說明。
?
在上面這個例子中,有三個ES 的 node ,其中每一個 index 中包含兩個 primary?shard ,每個 primary?shard 擁有一個 replica?shard 。下面從幾種常見的數據操作來說明二者之間的交互情況。
?
- 索引與刪除一個文檔
?
?
這兩種過程均可以分為三個過程來描述:
階段 1 :客戶端發送了一個索引或者刪除的請求給 node?1 。
?
階段 2 : node?1 通過請求中文檔的? _id? 值判斷出該文檔應該被存儲在 shard?0? 這個分片中,并且 node?1 知道 shard?0 的 primary?shard 位于 node?3 這個節點上。因此 node?1 會把這個請求轉發到 node?3 。
?
階段 3 : node?3 在 shard?0? 的 primary?shard 上執行請求。如果請求執行成功,它 node?3 將并行地將該請求發給 shard?0 的其余所有 replica?shard 上,也就是存在于 node?1 和 node?2 中的 replica?shard 。如果所有的 replica?shard 都成功地執行了請求,那么將會向 node?3 回復一個成功確認,當 node?3 收到了所有 replica?shard 的確認信息后,則最后向用戶返回一個 Success 的消息。
?
- 更新一個文檔
?
?
該過程可以分為四個階段來描述:
階段 1 :客戶端向 node?1 發送一個文檔更新的請求。
?
階段 2 :同樣的 node?1 通過請求中文檔的? _id? 值判斷出該文檔應該被存儲在 shard?0? 這個分片中,并且 node?1 知道 shard?0 的 primary?shard 位于 node?3 這個節點上。因此 node?1 會把這個請求轉發到 node?3 。
?
階段 3 : node?3 從文檔所在的 primary?shard 中獲取到它的 JSON 文件,并修改其中的 _source 中的內容,之后再重新索引該文檔到其 primary?shard 中。
?
階段 4 :如果 node?3 成功地更新了文檔, node?3 將會把文檔新的版本并行地發給其余所有的 replica?shard 所在 node 中。這些 node 也同樣重新索引新版本的文檔,執行后則向 node?3 確認成功,當 node?3 接收到所有的成功確認之后,再向客戶端發送一個更新成功的信息。
?
?
- 檢索文檔
CRUD 這些操作的過程中一般都是結合一些唯一的標記例如: _index , _type ,以及 routing 的值,這就意味在執行操作的時候都是確切的知道文檔在集群中的哪個 node 中,哪個 shard 中。
而檢索過程往往需要更多的執行模式,因為我們并不清楚所要檢索的文檔具體位置所在,?它們可能存在于 ES 集群中個任何位置。因此,一般情況下,檢索的執行不得不去詢問 index 中的每一個 shard 。
但是,找到所有匹配檢索的文檔僅僅只是檢索過程的一半,在向客戶端返回一個結果列表之前,必須將各個 shard 發回的小片的檢索結果,拼接成一個大的已排好序的匯總結果列表。正因為這個原因,檢索的過程將分為查詢階段與獲取階段( Query?Phase?and?Fetch?Phase )。
?
- Query?Phase
在最初的查詢過程中,查詢請求會廣播到 index 中的每一個 primary?shard 和 replica?shard 中,每一個 shard 會在本地執行檢索,并建立一個優先級隊列( priority?queue )。這個優先級隊列是一個根據文檔匹配度這個指標所排序列表,列表的長度由分頁參數 from 和 size 兩個參數所決定。例如:
?
?
下面從一個例子中說明這個過程:
?
Query?Phase 階段可以再細分成 3 個小的子階段:
子階段 1 :客戶端發送一個檢索的請求給 node?3 ,此時 node?3 會創建一個空的優先級隊列并且配置好分頁參數 from 與 size 。
?
子階段 2 : node?3 將檢索請求發送給該 index 中個每一個 shard (這里的每一個意思是無論它是 primary 還是 replica ,它們的組合可以構成一個完整的 index 數據)。每個 shard 在本地執行檢索,并將結果添加到本地優先級隊列中。
?
子階段 3 :每個 shard 返回本地優先級序列中所記錄的 _id 與 sort 值,并發送 node?3 。 Node?3 將這些值合并到自己的本地的優先級隊列中,并做全局的排序。
?
- Fetch?Phase
Query?Phase 主要定位了所要檢索數據的具體位置,但是我們還必須取回它們才能完成整個檢索過程。而 Fetch?Phase 階段的任務就是將這些定位好的數據內容取回并返回給客戶端。
?
同樣也用一個例子來說明這個過程:
?
Fetch?Phase 過程可以分為三個子過程來描述:
子階段 1 : node?3 獲取了所有待檢索數據的定位之后,發送一個 mget 的請求給與數據相關的 shard 。
?
子階段 2 :每個收到 node?3 的 get 請求的 shard 將讀取相關文檔 _source 中的內容,并將它們返回給 node?3 。
?
子階段 3 :當 node?3 獲取到了所有 shard 返回的文檔后, node?3 將它們合并成一條匯總的結果,返回給客戶端。
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
