shard_num = hash(_routing) % num_primary_shards
- _routing字段的取值,默認是_id字段
- num_primary_shards表示索引有多少個shard
最終得到這條數據應該在被分配在那個一個shard上,也就是說默認是基於hash的分片,保證在每個shard上數據量都近似平均,這樣就不會出現負載不均衡的情況,然后在檢索的時候,es默認會搜索所有shard上的數據,最后在master節點上匯聚在處理后,返回最終數據。
實際應用中的場景:比如說存儲一年的數據,如果按hash去索引,那就是分布非常均勻,這樣的話無論查詢什么數據都會去所有的shard上查詢,如果數據量比較大,那么響應速度就比較慢,但這時,一年12個月的數據本身分布並不均勻,有幾個月的數據偏多,有幾個月的數據偏少,理想情況下,數據偏少的月,查詢性能應該更快,但如果是基於hash分片,那么我們並不能實現這種需求,因為hash分片,查詢時候必須要命中所有shard之后,查詢的結果才是准的,這樣以來,每次查詢都要掃描所有shard,比如我已經知道數據本身就是1月份的,那其實最好的情況下,只查詢1月的數據就行,而不需要把一年的數據都掃描一遍,導致最終的結果就是慢的更慢,快的也慢,所以我們要針對性的做優化。
思路也比較明確了,那就是按照月份分區,每一個月的數據都存在指定的分區中,如果是mysql那就是每個月份一張表,然后查詢時候,直接查詢對應月份的數據即可,在es和solr中原理也大致如此,唯一不同的地方在於es和solr都比較方便的支持了路由字段的設置而如果是數據庫,則需要自己通過中間件的方式來搞定。
在es中使用路由字段,先看一個官網給的簡單的例子:
PUT my_index/my_type/1?routing=user1&refresh=true { "title": "This is a document" } GET my_index/my_type/1?routing=user1
GET my_index/_search { "query": { "terms": { "_routing": [ "user1" ] } } }
除此之外,路由字段,也可以指定多個:
GET my_index/_search?routing=user1,user2 { "query": { "match": { "title": "document" } } }
PUT my_index2 { "mappings": { "my_type": { "_routing": { "required": true } } } } PUT my_index2/my_type/1 { "text": "No routing value provided" }
缺失路由字段會拋出異常:
routing_missing_exception
- elasticsearch直接通過hash值取模然后除以routingFactor來確定所屬的shard,而solr中必須要遍歷索引下的每個shard才能確定所屬shard。從效率看如果有n個shard,那么solr的時間復雜度為O(n),而elasticserach的時間復雜度為O(1)。
- 對於shard數比較大,索引數據很多的情況下,elasticsearch會快上不少。
- elasticsearch不支持單個shard split, 而solr支持
參考資料: