ES學習之分片路由
本文主要內容:
1、路由一個文件到一個分片
2、新建、索引和刪除請求
3、取回單個文件
4、區域性單個文件
5、多文件模式
6、理解一下ES深度分頁(from-size)的劣勢
路由一個文件到一個分片
當索引一個文件的時候,文件會被儲存到一個主分片中。 Elasticsearch 如何知道一個文件應該存放到哪個分片中呢?當我們建立文件時,它如何決定這個文件應當被儲存在分片 1 還是分片 2 中呢?
首先這肯定不會是隨機的,否則將來要獲取文件的時候我們就不知道從何處尋找了。實際上,這個過程是根據下面這個公式決定的:
shard = hash(routing) % number_of_primary_shards
routing
是一個可變值,預設是文件的 _id
,也可以設定成一個自定義的值。 routing
通過 hash
函式生成一個數字,然後這個數字再除以 number_of_primary_shards
(主分片的數量)後得到餘數 。這個分佈在 0 到 number_of_primary_shards-1
之間的餘數,就是我們所尋求的文件所在分片的位置。
這就解釋了為什麼我們要在建立索引的時候就確定好主分片的數量 並且永遠不會改變這個數量:因為如果數量變化了,那麼所有之前路由的值都會無效,文件也再也找不到了。
所有的文件 API( get 、 index 、 delete 、 bulk 、 update 以及 mget )
routing
的路由引數 ,通過這個引數我們可以自定義文件到分片的對映。一個自定義的路由引數可以用來確保所有相關的文件——例如所有屬於同一個使用者的文件——都被儲存到同一個分片中。我們也會在擴容設計這一章中詳細討論為什麼會有這樣一種需求。
主分片和副分片如何互動:
為了說明目的, 我們 假設有一個叢集由三個節點組成。 它包含一個叫 blogs 的索引,有兩個主分片,每個主分片有兩個副本分片。相同分片的副本不會放在同一節點,所以我們的叢集看起來像 圖 8 “有三個節點和一個索引的叢集”。
我們可以傳送請求到叢集中的任一節點。 每個節點都有能力處理任意請求。 每個節點都知道叢集中任一文件位置,所以可以直接將請求轉發到需要的節點上。 在下面的例子中,將所有的請求傳送到 Node 1 ,我們將其稱為 協調節點(coordinating node) 。
當傳送請求的時候, 為了擴充套件負載,更好的做法是輪詢叢集中所有的節點。
新建、索引和刪除請求
新建、索引和刪除請求都是寫操作,必須在主分片上面完成之後才能被複制到相關的副本分片,如下圖所示圖 9 “新建、索引和刪除單個文件”.
以下是在主副分片和任何副本分片上面成功新建,索引和刪除文件所需要的步驟順序:
- 客戶端向
Node 1
傳送新建、索引或者刪除請求。 - 節點使用文件的
_id
確定文件屬於分片 0
。請求會被轉發到Node 3
,因為分片 0 的主分片目前被分配在Node 3 上。 - Node 3在主分片上面執行請求。如果成功了,它將請求並行轉發到Node 1和Node 2
的副本分片上。一旦所有的副本分片都報告成功, Node 3將向協調節點報告成功,協調節點向客戶端報告成功。
在客戶端收到成功響應時,文件變更已經在主分片和所有副本分片執行完成,變更是安全的。
consistency
consistency,即一致性。在預設設定下,即使僅僅是在試圖執行一個寫操作之前,主分片都會要求必須要有規定數量(quorum)(或者換種說法,也即必須要有大多數)的分片副本處於活躍可用狀態,才會去執行寫操作(其中分片副本可以是主分片或者副本分片)。這是為了避免在發生網路分割槽故障(network partition)的時候進行寫操作,進而導致資料不一致。規定數量即:
int( (primary + number_of_replicas) / 2 ) + 1
consistency
引數的值可以設為 one (只要主分片狀態 ok 就允許執行寫操作),all(必須要主分片和所有副本分片的狀態沒問題才允許執行寫操作)
, 或 quorum 。預設值為 quorum , 即大多數的分片副本狀態沒問題就允許執行寫操作。
注意,規定數量的計算公式中number_of_replicas
指的是在索引設定中的設定副本分片數,而不是指當前處理活動狀態的副本分片數。如果你的索引設定中指定了當前索引擁有三個副本分片,那規定數量的計算結果即:
int( (primary + 3 replicas) / 2 ) + 1 = 3
如果此時你只啟動兩個節點,那麼處於活躍狀態的分片副本數量就達不到規定數量,也因此您將無法索引和刪除任何文件。
timeout
如果沒有足夠的副本分片會發生什麼? Elasticsearch會等待,希望更多的分片出現。預設情況下,它最多等待1分鐘。 如果你需要,你可以使用 timeout 引數 使它更早終止: 100 100毫秒,30s 是30秒。
取回單個文件:
以下是從主分片或者副本分片檢索文件的步驟順序:
- 客戶端向
Node 1
傳送獲取請求。 - 節點使用文件的
_id
來確定文件屬於分片 0 。分片 0 的副本分片存在於所有的三個節點上。 在這種情況下,它將請求轉發到Node 2
Node 2
將文件返回給Node 1
,然後將文件返回給客戶端。
在處理讀取請求時,協調結點在每次請求的時候都會通過輪詢所有的副本分片來達到負載均衡。
在文件被檢索時,已經被索引的文件可能已經存在於主分片上但是還沒有複製到副本分片。 在這種情況下,副本分片可能會報告文件不存在,但是主分片可能成功返回文件。 一旦索引請求成功返回給使用者,文件在主分片和副本分片都是可用的。
區域性單個文件
以下是部分更新一個文件的步驟:
- 客戶端向
Node 1
傳送更新請求。 - 它將請求轉發到主分片所在的
Node 3
。 Node 3
從主分片檢索文件,修改_source
欄位中的 JSON ,並且嘗試重新索引主分片的文件。
如果文件已經被另一個程序修改,它會重試步驟 3 ,超過retry_on_conflict
次後放棄。- 如果
Node 3
成功地更新文件,它將新版本的文件並行轉發到Node 1
和Node 2
上的副本分片,重新建立索引。一旦所有副本分片都返回成功,Node 3
向協調節點也返回成功,協調節點向客戶端返回成功。
多文件模式
mget
和 bulk API
的模式類似於單文件模式。區別在於協調節點知道每個文件存在於哪個分片中。 它將整個多文件請求分解成 每個分片 的多文件請求,並且將這些請求並行轉發到每個參與節點。
協調節點一旦收到來自每個節點的應答,就將每個節點的響應收集整理成單個響應,返回給客戶端,如
以下是使用單個 mget
請求取回多個文件所需的步驟順序:
- 客戶端向
Node 1
傳送mget
請求。 Node 1
為每個分片構建多文件獲取請求,然後並行轉發這些請求到託管在每個所需的主分片或者副本分片的節點上。一旦收到所有答覆,Node 1
構建響應並將其返回給客戶端。 可以對docs
陣列中每個文件設定routing
引數。
bulk API
, 允許在單個批量請求中執行多個建立、索引、刪除和更新請求。
bulk API 按如下步驟順序執行:
- 客戶端向
Node 1
傳送bulk
請求。 Node 1
為每個節點建立一個批量請求,並將這些請求並行轉發到每個包含主分片的節點主機。主分片一個接一個按順序執行每個操作。當每個操作成功時,主分片並行轉發新文件(或刪除)到副本分片,然後執行下一個操作。一旦所有的副本分片報告所有操作成功,該節點將向協調節點報告成功,協調節點將這些響應收集整理並返回給客戶端。
bulk API 還可以在整個批量請求的最頂層使用 consistency
引數,以及在每個請求中的元資料中使用 routing
引數。
理解一下ES深度分頁(from-size)的劣勢:
理解為什麼深度分頁是有問題的,我們可以假設在一個有 5 個主分片的索引中搜索。 當我們請求結果的第一頁(結果從 1 到 10 ),每一個分片產生前 10 的結果,並且返回給 協調節點 ,協調節點對 50 個結果排序得到全部結果的前 10 個。
現在假設我們請求第 1000 頁–結果從 10001 到 10010 。所有都以相同的方式工作除了每個分片不得不產生前10010個結果以外。 然後協調節點對全部 50050 個結果排序最後丟棄掉這些結果中的 50040 個結果。
可以看到,在分散式系統中,對結果排序的成本隨分頁的深度成指數上升。這就是 web 搜尋引擎對任何查詢都不要返回超過 1000 個結果的原因。