Elasticsearch 項目中使用到Es的父子結構、在數據填充之后,查看每個節點的數據分布情況,發現有的節點數據多,有的節點少的情況,在未使用Es父級結構之前,每個節點的數據分布還算平均,如下圖:
左邊的數據是未使用父子結構之前每個節點的數據分布數量,右邊的是使用了父子結構之后的數據節點分布數量,最下面一行紅色的數字是節點平均數量,可以看出,左邊的數據與平均值相差不大,右邊的數據與平均值最大相差400萬,這個差距還是蠻大的,為什么會有這么大的差距呢?圍繞着這個問題,進行了一番研究,今天就來學習學習下Elasticsearch 的路由機制。
首先,Es的路由機制與其分片機制有着直接的關系,Es的路由機制是通過哈希算法,將具有相同哈希值的文檔放置在同一分片中的,通過這個哈希算法來進行負載均衡的效果,這個就是為什么左側圖中的每個節點的數據都與平均值相差不大的原因。
計算公式是:
shard = hash(routing) % number_of_primary_shards
routing 值是一個任意字符串,它默認是_id
但也可以自定義。這個routing 字符串通過哈希函數生成一個數字,然后除以主切片的數量得到一個余數(remainder),余數的范圍永遠是0
到number_of_primary_shards - 1
,這個數字就是特定文檔所在的分片。
所以,每一條數據在寫入的時候,是放在分片1上,還是分片2上,不是瞎蒙的,是通過計算得來的。這也解釋了為什么主分片的數量只能在創建索引時定義且不能修改:如果主分片的數量在未來改變了,所有先前的路由值就失效了,文檔也就永遠找不到了。
上面解釋清楚了,在來解釋下,為什么我使用了Es父子結構之后,每個節點的數據發生了那么大的差距呢?
解釋這個之前,先來看下Es父子結構情況下,寫入子數據的寫法(怎么創建父子結構這里就不贅述了):
PUT /company/employee/1?parent=london { "name": "Alice Smith", "dob": "1970-10-24", "hobby": "hiking" }
在創建子文檔的時候你必須指出他們的父文檔的id,為什么要指定父文檔的ID呢?這是因為Es在存儲子數據的時候,會用父文檔的ID去計算存放在那個分片,它會把父文檔相同的數據都存放在同一個分片上面;
舉個例子,如果父文檔1,下面子文檔有10個,存儲在A分片;
分文檔2,下面子文檔有500個,存儲在B分片上;
這樣,隨着每個父文檔對應的子文檔數據分布不均,節點的數據量就會越來越不均衡,這就說明了為什么使用了Es父子結構之后,節點數據差距較大的情況了。
問題來了,那么怎么解決分布不均的問題呢?
可以通過指定個性化路由來處理,所有的文檔API(get,index,delete,update和mget)都能接收一個routing參數,可以用來形成個性化文檔分片映射。
寫入數據時怎么指定存儲的分片,可以參考這篇:https://www.cnblogs.com/bonelee/p/6055340.html ,別人已經寫好,我就不重新去寫了。
來說一說設置分片的好處,我畫了個圖,舉個例子,現在你去找一本計算機相關的圖書,現在有四個節點,在沒有指定分片存儲之前,所有的書是平均放的,每一個節點上面都會有計算機相關的圖書,
然后,Es就會去四個節點上面分別去找,黑色的線,ES會向每個節點都發送一個請求,然后節點就開始找找,找呀找,然后,找到了,節點開始返回數據了,就是紅色的線;
ES把所有的節點的響應數據都匯總之后,然后在按照評分去做下排序,把評分最高,相關性比較高的數據在返回給你。
那么,如果在存儲的時候,就告訴ES,把所有計算機相關的書籍都給我放置到節點3上面,然后在查詢的時候,告訴ES,在節點3上面給我找計算機相關的書籍。如下圖:
很明顯,這樣的查詢,就不會去向所有的節點都廣播,定位很精准,在某一程度上,效率可能會高一點,但是,這樣做的話,就會增加ES節點分片的維護成本,就違背了Es高可用性,拓展性的設計理念了
所以,要根據具體業務具體需求去定響應的方案。