學完ES分布式集群的工作原理以及一些基本的將數據放入索引然后檢索它們的所有方法,我們可以繼續學習在分布式系統中,每個分片的文檔是被如何索引和查詢的。
路由
首先,我們需要明白,文檔和分片之間是如何匹配的,這就是路由。當你索引一個文檔,它被存儲在單獨一個主分片上。Elasticsearch是如何知道文檔屬於哪個分片的呢?當你創建一個新文檔,它是如何知道是應該存儲在分片1還是分片2上的呢?
進程不能是隨機的,因為我們將來要檢索文檔。事實上,它根據一個簡單的算法決定:
shard = hash(routing) % number_of_primary_shards
routing
值是一個任意字符串,它默認是_id
但也可以自定義。這個routing
字符串通過哈希函數生成一個數字,然后除以主切片的數量得到一個余數(remainder
),余數的范圍永遠是0
到number_of_primary_shards - 1
,這個數字就是特定文檔所在的分片。
這也解釋了為什么主分片的數量只能在創建索引時定義且不能修改:如果主分片的數量在未來改變了,所有先前的路由值就失效了,文檔也就永遠找不到了。
所有的文檔API(get、index、delete、bulk、update、mget)都接收一個routing參數,它用來自定義文檔到分片的映射。自定義路由值可以確保所有相關文檔——例如屬於同一個人的文檔——被保存在同一分片上。
例如,可以這樣設置參數:
POST twitter/tweet?routing=kimchy
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
主分片和復制分片如何交互
在文檔確認存儲到哪個主分片以后,接下來就是主分片將數據復制到復制分片的任務,為了闡述意圖,我們假設有三個節點的集群。它包含一個叫做blogs
的索引並擁有兩個主分片。每個主分片有兩個復制分片。相同的分片不會放在同一個節點上,所以我們的集群是這樣的:
我們能夠發送請求給集群中任意一個節點。每個節點都有能力處理任意請求。每個節點都知道任意文檔所在的節點,所以也可以將請求轉發到需要的節點。下面的例子中,我們將發送所有請求給Node 1,這個節點我們將會稱之為請求節點(requesting node)。一般情況下,當我們發送請求,最好的做法是循環通過所有節點請求,這樣可以平衡負載。
新建、索引和刪除文檔
新建、索引和刪除請求都是寫(write)操作,它們必須在主分片上成功完成才能復制到相關的復制分片上。
下面是在主分片和復制分片上成功新建、索引或刪除一個文檔必要的順序步驟:
- 客戶端給Node 1發送新建、索引或刪除請求。
- 節點使用文檔的_id確定文檔屬於分片0。它轉發請求到Node 3,分片0位於這個節點上。
- Node 3在主分片上執行請求,如果成功,它轉發請求到相應的位於Node 1和Node 2的復制節點上。當所有的復制節點報告成功,Node 3報告成功到請求的節點,請求的節點再報告給客戶端。
客戶端接收到成功響應的時候,文檔的修改已經被應用於主分片和所有的復制分片。你的修改生效了。
有很多可選的請求參數允許你更改這一過程。你可能想犧牲一些安全來提高性能。這些選項很少使用因為Elasticsearch已經足夠快。
注意:下面的參數只對ElasticSearch 5.0以下的版本有效,在ElasticSearch 5.0之后貌似使用wait_for_active_shards
代替了consistency
。所以之前的參數了解即可,實際可以參考:Create Index—Wait For Active Shards。
replication(注意在ElasticSearch 5.0開始被廢棄)
復制默認的值是sync
。這將導致主分片得到復制分片的成功響應后才返回。
如果你設置replication
為async
,請求在主分片上被執行后就會返回給客戶端。它依舊會轉發請求給復制節點,但你將不知道復制節點成功與否。
上面的這個選項不建議使用。默認的sync
復制允許Elasticsearch強制反饋傳輸。async
復制可能會因為在不等待其它分片就緒的情況下發送過多的請求而使Elasticsearch過載。
consistency(注意在ElasticSearch 5.0開始被廢棄)
默認主分片在嘗試寫入時需要規定數量(quorum
)或過半的分片(可以是主節點或復制節點)可用。這是防止數據被寫入到錯的網絡分區。規定的數量計算公式如下:
int( (primary + number_of_replicas) / 2 ) + 1
consistency
允許的值為one
(只有一個主分片),all
(所有主分片和復制分片)或者默認的quorum
或過半分片。
注意number_of_replicas
是在索引中的的設置,用來定義復制分片的數量,而不是現在活動的復制節點的數量。如果你定義了索引有3個復制節點,那規定數量是:
int( (primary + 3 replicas) / 2 ) + 1 = 3
但如果你只有2個節點,那你的活動分片不夠規定數量,也就不能索引或刪除任何文檔。
注意:
- 新索引默認有1個復制分片,這意味着為了滿足
quorum
的要求需要兩個活動的分片。當然,這個默認設置將阻止我們在單一節點集群中進行操作。為了避開這個問題,規定數量只有在number_of_replicas
大於一時才生效。 - 一個疑惑,是不是
primary
值一直都只會是1???
wait_for_active_shards(新參數)
在ElasticSearch 5.0中可以用wait_for_active_shards
參數表示:等待活動的分片,具體的值和consistency
類似,下面用wait_for_active_shards
演示一個實際使用的例子。
開始我們先設置一個新的索引:
PUT /active
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 3
}
}
我們默認先只打開兩個節點,等下我們設置wait_for_active_shards
值為3,按照上面講解的我們如果只有兩個節點,那么活動的分片最多也就2個,所以是不夠的,會等待新的活動節點的到來。(這里我們只能通過少一個節點的方法演示缺少活動分片,因為我們不方便演示出讓某個分片處於不活動的狀態。)
因為我們只有兩個節點,所以活動的分片最多也只有兩個。下面我們執行文檔存儲操作,並且添加參數wait_for_active_shards=3
:
可以發現,確實開始處於等待狀態,沒有馬上返回結果,下面我們參數開啟第三個節點,讓索引擁有第三個活動分片:
可以看到一旦我們的節點開啟,文檔的存儲馬上就會返回成功。
教程中關於這部分網上很多朋友不太理解,我們可以通過查看官方文檔和實踐去證明自己的想法,希望上面的分析大家可以理解一些,還有不對的地方大家可以一起學習。
timeout
當分片副本不足時會怎樣?Elasticsearch會等待更多的分片出現。默認等待一分鍾。如果需要,你可以設置timeout參數讓它終止的更早:100表示100毫秒,30s表示30秒。
檢索文檔
文檔能夠從主分片或任意一個復制分片被檢索。
下面我們羅列在主分片或復制分片上檢索一個文檔必要的順序步驟:
- 客戶端給Node 1發送get請求。
- 節點使用文檔的_id確定文檔屬於分片0。分片0對應的復制分片在三個節點上都有。此時,它轉發請求到Node 2。
- Node 2返回文檔(document)給Node 1然后返回給客戶端。
對於讀請求,為了平衡負載,請求節點會為每個請求選擇不同的分片——它會循環所有分片副本(包括主分片)。
可能的情況是,一個被索引的文檔已經存在於主分片上卻還沒來得及同步到復制分片上。這時復制分片會報告文檔未找到,主分片會成功返回文檔。一旦索引請求成功返回給用戶,文檔則在主分片和復制分片都是可用的。
局部更新文檔
update
API結合了之前提到的讀和寫的模式。
下面我們羅列執行局部更新必要的順序步驟:
- 客戶端給Node 1發送更新請求。
- 它轉發請求到主分片所在節點Node 3。
- Node 3從主分片檢索出文檔,修改_source字段的JSON,然后在主分片上重建索引。如果有其他進程修改了文檔,它以
retry_on_conflict
設置的次數重復步驟3,都未成功則放棄。 - 如果Node 3成功更新文檔,它同時轉發文檔的新版本到Node 1和Node 2上的復制節點以重建索引。當所有復制節點報告成功,Node 3返回成功給請求節點,然后返回給客戶端。
update
API還接受routing
、replication
(棄)、consistency
(棄)和timout
參數。
基於文檔的復制
當主分片轉發更改給復制分片時,並不是轉發更新請求,而是轉發整個文檔的新版本。記住這些修改轉發到復制節點是異步的,它們並不能保證到達的順序與發送相同。如果Elasticsearch轉發的僅僅是修改請求,修改的順序可能是錯誤的,那得到的就是個損壞的文檔。
多文檔模式
mget
和bulk
API與單獨的文檔類似。差別是請求節點知道每個文檔所在的分片。它把多文檔請求拆成每個分片的對文檔請求,然后轉發每個參與的節點。
一旦接收到每個節點的應答,然后整理這些響應組合為一個單獨的響應,最后返回給客戶端。
下面我們將羅列通過一個mget
請求檢索多個文檔的順序步驟:
- 客戶端向Node 1發送
mget
請求。 - Node 1為每個分片構建一個多條數據檢索請求,然后轉發到這些請求所需的主分片或復制分片上。當所有回復被接收,Node 1構建響應並返回給客戶端。
routing 參數可以被docs中的每個文檔設置。
下面我們將羅列使用一個bulk
執行多個create
、index
、delete
和update
請求的順序步驟:
- 客戶端向Node 1發送
bulk
請求。 - Node 1為每個分片構建批量請求,然后轉發到這些請求所需的主分片上。
- 主分片一個接一個的按序執行操作。當一個操作執行完,主分片轉發新文檔(或者刪除部分)給對應的復制節點,然后執行下一個操作。一旦所有復制節點報告所有操作已成功完成,節點就報告success給請求節點,后者(請求節點)整理響應並返回給客戶端。
bulk
API還可以在最上層使用replication
(棄)和consistency
(棄)參數,routing
參數則在每個請求的元數據中使用。
總結
以上就是關於在分布式系統中,每個分片的文檔是被如何索引和查詢的。雖然版本的更新有一些參數會更新,但是整體的內部實現應該不會有太大的變化,分享一個學習方法,學習的時候把新舊的版本內容通過對比,不僅可以更好理解知識,而且可以加深印象。更何況舊的不會被很快淘汰,學了又何妨!
轉載請注明出處。
作者:wuxiwei
出處:http://www.cnblogs.com/wxw16/p/6192549.html