Elasticsearch 對文檔操作時的分片交互過程分析

Elasticsearch 對文檔操作時的分片交互過程分析

路由文檔到分片

1文檔路由到分片上:一個索引由多個分片構成,當添加(刪除、修改)一個文檔時,Elasticsearch就需要決定這個文檔存儲在哪個分片上,這個過程就稱為數據路由(routing)。

2 路由算法:

shard = hash(routing) % number_of_primary_shards

示例:一個索引,3個 primary shard

1 每次增刪改查時,都有一個 routing 值,默認是文檔的 _id 的值。

2 對這個 routing 值使用 hash 函數進行計算。

3計算出的值再和主分片個數取余數,余數的取值范圍永遠是(0 ~ number_of_primary_shards - 1)之間,文檔就在對應的 shard 上。routing 值默認是文檔的 _id 的值,也可以手動指定一個值,手動指定對于負載均衡以及提升批量讀取的性能都有幫助。

4 正是這種路由機制,導致了 primary shard(主分片)的個數為什么在索引建立之后不能修改。對已有索引主分片數目的修改直接會導致路由規則出現嚴重問題,部分數據將無法被檢索

增刪改查時主分片與復制分片如何交互

假設有三個節點的集群。它包含一個叫做bblogs的索引并擁有兩個主分片。每個主分片有兩個復制分片。相同的分片不會放在同一個節點上,所以我們的集群是這樣的:

我們能夠發送請求給集群中任意一個節點。每個節點都有能力處理任意請求。每個節點都知道任意文檔所在的節點,所以也可以將請求轉發到需要的節點。下面的例子中,我們將發送所有請求給Node 1,這個節點我們將會稱之為請求節點(requesting node)。

新建、 索引與刪除一個文檔

新建、索引和刪除請求都是寫(write)操作,它們必須在主分片上成功完成才能復制到相關的復制分片上。

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的消息。

客戶端接收到成功響應的時候,文檔的修改已經被應用于主分片和所有的復制分片。此時修改已經生效。

有很多可選的請求參數允許你更改這一過程。你可能想犧牲一些安全來提高性能。這一選項很少使用因為Elasticsearch已經足夠快,下面將一一闡述。

replication

復制默認的值是 sync(同步操作)。這將導致主分片得到復制分片的成功響應后才返回。

當 replication 設置為 async(異步操作),請求在主分片上被執行后就會返回給客戶端。它依舊會轉發請求給復制節點,但你將不知道復制節點成功與否。

該選項不建議使用。默認的sync復制允許Elasticsearch強制反饋傳輸。async復制可能會因為在不等待其它分片就緒的情況下發送過多的請求而使Elasticsearch過載。

consistency(寫一致性原理和quorum機制)

默認主分片在嘗試寫入時需要規定數量(quorum)或過半的分片(可以是主節點或復制節點)可用。這是防止數據被寫入到錯的網絡分區。規定的數量計算公式如下:

int( (primary + number_of_replicas) / 2 ) + 1

consistency允許的值為one(只有一個主分片),all(所有主分片和復制分片)或者默認的quorum或過半分片。

注意number_of_replicas是在索引中的的設置,用來定義復制分片的數量,而不是現在活動的復制節點的數量。如果你定義了索引有3個復制節點,那規定數量是:

int( (1 primary + 3 replicas) / 2 ) + 1 = 3

但如果你只有2個節點,那你的活動分片不夠規定數量,也就不能索引或刪除任何文檔。

timeout

當分片副本不足時會怎樣?

Elasticsearch會等待更多的分片出現。默認等待一分鐘。如果需要,你可以設置timeout參數讓它終止的更早:100表示100毫秒,30s表示30秒。

注意:

新索引默認有1個復制分片,這意味著為了滿足quorum(規定數量)的要求需要兩個活動的分片。當然,這個默認設置將阻止我們在單一節點集群中進行操作。為了避開這個問題,規定數量只有在number_of_replicas大于一時才生效。

局部更新文檔

update API 結合了之前提到的讀和寫模式。

該過程可以分為四個階段來描述:

1? 客戶端給node 1發送更新請求。

2? 它轉發請求到主分片所在節點node 3。

3? node 3從主分片檢索出文檔,修改_source字段的JSON,然后在主分片上重建索引。如果有其他進程修改了文檔,它以retry_on_conflict設置的次數重復步驟3,都未成功則放棄。

4? 如果node 3成功更新文檔,它同時轉發文檔的新版本到Node 1和Node 2上的復制節點以重建索引。當所有復制節點報告成功,node 3返回成功給請求節點,然后返回給客戶端。

update API還接受 新建、索引和刪除 提到的routing、replication、consistency和timout參數。

基于文檔的復制:

當主分片轉發更改給復制分片時,并不是轉發更新請求,而是轉發整個文檔的新版本。記住這些修改轉發到復制節點是異步的,它們并不能保證到達的順序與發送相同。如果Elasticsearch轉發的僅僅是修改請求,修改的順序可能是錯誤的,那得到的就是個損壞的文檔。

檢索文檔

文檔能夠從主分片或任意一個復制分片被檢索。

下面我們羅列在主分片或復制分片上檢索一個文檔必要的順序步驟:

1? 客戶端給node 1發送get請求,node1節點就成了 coordinating node(協同節點),該節點使用路由算法算出文檔所在的 primary shard;協調節點把請求轉發給 primary shard ,也可以轉發給 replica shard(使用輪詢調度算法 [ Round-Robin Scheduling,把請求平均分配至 primary shard 和 replica shard ] ,場景在于多個相同請求進入時)。

2? 節點使用文檔的_id確定文檔屬于分片0。分片0對應的復制分片在三個節點上都有。此時,它轉發請求到node 2。

3? node 2返回文檔(document)給node 1然后返回給客戶端。

對于讀請求,為了平衡負載,請求節點會為每個請求選擇不同的分片——它會循環所有分片副本。

特殊情況:一個被索引的文檔已經存在于主分片上卻還沒來得及同步到復制分片上。這時復制分片會報告文檔未找到,主分片會成功返回文檔。一旦索引請求成功返回給用戶,文檔則在主分片和復制分片都是可用的。

Query Phase & Fetch Phase

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兩個參數所決定。例如:

GET /_search

{

"from": 90,

"size": 10

}

下面從一個例子中說明這個過程:

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將它們合并成一條匯總的結果,返回給客戶端。

多文檔模式

mget和bulk API與單獨的文檔類似。差別是請求節點知道每個文檔所在的分片。它把多文檔請求拆成每個分片的對文檔請求,然后轉發每個參與的節點。

一旦接收到每個節點的應答,然后整理這些響應組合為一個單獨的響應,最后返回給客戶端。

下面我們將羅列通過一個mget請求檢索多個文檔的順序步驟:

1 客戶端向node 1發送mget請求。

2? node 1為每個分片構建一個多條數據檢索請求,然后轉發到這些請求所需的主分片或復制分片上。當所有回復被接收,node 1構建響應并返回給客戶端。

routing 參數可以被docs中的每個文檔設置。

下面我們將羅列使用一個bulk執行多個create、index、delete和update請求的順序步驟:

1 客戶端向node 1發送bulk請求。

2? node 1為每個分片構建批量請求,然后轉發到這些請求所需的主分片上。

3主分片一個接一個的按序執行操作。當一個操作執行完,主分片轉發新文檔(或者刪除部分)給對應的復制節點,然后執行下一個操作。一旦所有復制節點報告所有操作已成功完成,節點就報告success給請求節點,后者(請求節點)整理響應并返回給客戶端。

bulk API還可以在最上層使用replication和consistency參數,routing參數則在每個請求的元

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容