almost 2 years ago

上一篇文章我們詳細的說明完分片的機制後,接下來我們就要來詳細的說明片鍵的選擇,片鍵的選擇關係到你的分片執行速度與效能,並且一但建立後,要再修改幾乎是不太可能的,所以請像選老婆一樣,用心的選~

  • 完美的片鍵定義(這鬼不存在的)
  • 片鍵的種類

~ 完美的片鍵定義(這鬼不存在) ~

在我們開始學習片鍵的選擇前,我們要先知道,什麼樣的片鍵是最好的,最理想的,但想也知道最好的東西是不存在的,但我們還是要知道,才能給我們的選擇給個基準。

整體來說完美的片鍵有下面特性

  • 所有的新增、刪除、更新等寫的操作,都可以平均分配到所有的分片。
  • 所有的搜尋等讀的操作,都可以平均分配到所有的分片。
  • 所有的操作都只會發到相關的分片,例如更新時不會跑去分片內是空的進行更新。

我們先來說說第一個特性,如果沒有該項特性會發生什麼事情,假設我們的cluster有四個分片,我們當然是希望每個分片可以處理25%的事情,但是假設我們做那些寫的操作時(ex.新增)全部都集中在其中一個分片,那你會發覺那個分片會越來越大~越來越大~ ,而且別忘了我們上章節說的chunk分配,它是根據數量來進行分配,不是用大小,因此你的那個分片內的chunk不會分配到其它分片,這樣也就失去你用分片的意義了。

而至於第二點特性,mongos在處理搜尋請求時主要會分成下述兩種的處理方式。

  • 搜尋時不包含片鍵,則會將搜尋分配到發有的分片,然後合并搜尋結果,再返回給client
  • 搜尋時包含片鍵,則直接根據片鍵,然後找出要尋找的chunk,向相對的分片發送搜尋請求。

根據上面的說明,我們知道mongos的讀操作過程,然後我們這時在回來思考,如果這時搜尋時都集中在一個分片上,會發生什麼事,首先搜尋時不包含片鍵這種類型影響不大,但另一種就會影響到,因為原本該分散的壓力,反而都集中在一個分片,運氣不好搜尋請求過多,就爆掉了。

而至於第三點,就只是浪費資源囉~

這邊我們大概來整理一下,根據以上三點大概可以拆分成幾個良好片鍵的特性

  • 容易分割片鍵 : 容易分割的片鍵可以使mongodb更容易的均衡各分片的資料量,不容易發生過大的分片,基數越大的走容易分割資料。

基數是指系統將資料分成chunk的能力,例如性別欄位就是個低基數的例子,只有男與女。

  • 高隨機性的片鍵 : 具有越高隨機性的片鍵,他所分割出來的資料越容易均衡,也代表可避免任何一個分片承受過多的壓力。
  • 可以指向單個分片的片鍵 : 越可指向單個分片的片鍵,越能降低搜尋效能壓力,像是隨機型的片鍵就無法做到。

這世界沒有著麼美好的事情,基本上幾乎找不到完全符合上述的條件,所以相對的咱們只能選擇盡可能符合你需要的片鍵,而這時就只能根據你專案的需求來決定,例如說這專案是讀吃比較重還是寫吃比較重,比較最大的搜尋條件,或搜尋時間過久的搜尋,這時都是要考量的。

~ 一些片鍵的種類 ~

這邊開始我們就要來說明一些片鍵的種類。

升序片鍵

這種類型的片鍵,大部份都是欄位為Date類型或是ObjectId,是種會根據時間來增加欄位,或是根據先後順序進來的欄位。

假如我們使用這種欄位做為片鍵,會發生什麼事情 ? 一開始建立片鍵時你不會看到什麼問題,而是再於你新增時會發生,假設我們有下面的分片cluster

shard001 shard002 shard003
{min~2000} {2007~2008} {2014~2016}
{2001~2003} {2009~2010} {2017~max}
{2004~2006} {2011~2013}

然後這時我們要問個問題,我們進行新增時,它會加到那個chunk?

答案是{2017~max},所以也就是說,接來下你所有的新增操作,都只是在這個分片中進行,並且該chunk也會一直變大,然後在分拆、再分配,這樣會導致mongos壓力大因為會一直分配,而且所有的新增操作壓力都會放在這個分片上,這不是什麼好事。

呃對了順到說一下ObjectId為啥也是,因為它的組成有時間戳。

ObjectId = 時間戳 + 機器 + PID + 計數器

隨機分配的片鍵

這種類型的片鍵,大概就是類似選擇使用者名、電子郵件等,或是資料沒有規律的欄位,由於資料新增時是隨機分配的,所以各分片增加的速度會差不多,這也代表這mongos需要處理chunk分配的壓力下降不少。

但這種類型的片鍵有個缺點,那就是在尋找大量資料的情況下,效率不高,因為資料都是分散的必定需要去每個分片尋找,而如果是集中式的片鍵(類似等等要說的片鍵),就指定針對該分片進行尋找就好囉。

根據位置的片鍵

這類型的片鍵,基本上就是根據用戶的IP、經緯度、或是地址之類,不過所謂的位置定義,事實上不能以實體上的位置來定義,有時後也可以用比較相關的資料方在一起,這也是種方法。

這種類型的片鍵優點就是讀與寫操作時比較有效率,因為可以很明確的選擇某個分片,但缺點就是有可能會有某些分片資料很多,某些分片資料很少的狀況,例如假設片鍵根據縣市來進行分片,然後存使用者資料,這時會發現新北市那個分片資料量爆多的,因為它人口最多。

Hashed 片鍵

這種類型的片鍵讀與寫的操作能平均的分配,但缺點也是隨機分配片鍵的缺點,尋找大量資料時效率不高。

我們做個簡單的範例,假設我們有如下資料。

var objs = [];
for (var i=0;i<100000;i++){
    objs.push({"name":"user"+i});
}
db.users.insert(objs);

然後我們指行下面指令來建立hash索引,事實上我現在才知道有這種索引……。

db.users.ensureIndex({"name":"hashed"})

然後進行分片。

sh.shardCollection("test.users",{"name":"hashed"})

咱們來執行sh.status()看看結果,它會將name欄位的值進行hash然後等出hash值後在進行分片,其中這個NumberLong("-6854066565760758296")就是hash值,基本上都有唯一性,但是只一種情況下會有相同的值,那就數值型,例如1.99991這兩種hash值會相等,因為1.9999會取整數。

複合型片鍵

這樣類型的策略最適合用來處理需要大量搜尋範圍的應用,通常這種片鍵中的第一個值是比較低基數的鍵(最好是分片數的兩倍),而第二值就是我們上面介紹過的升序片鍵,假設我們有下列資料,state是個基數小的欄位,大概只有六個分區,然後另一個就是升序欄位Date,它是建立日期,它只會增加不會減,除非你自已偷改系統時間。

{ "state" : "AAA" , "date" : 20160101},
{ "state" : "BBB" , "date" : 20160102},
{ "state" : "CCC" , "date" : 20160103},
{ "state" : "DDD" , "date" : 20160104},
{ "state" : "EEE" , "date" : 20160105},
{ "state" : "AAA" , "date" : 20160106},
...
...
...
{ "state" : "AAA" , "date" : 20161231}

然後這時我們進行分片。

{ "state" : 1, "date" :1 }

它大置上會長成這樣,下面為其中一個chunk的範例,該chunk只會包含分區為AAABBB的並且它的日期是升序,也就是接下來新增的只會增加日期不會減少。

chunk1
範圍 {"AAA" ~ "BBB"}
document1 { "state" : "AAA" , "date" : 20160101}
document2 { "state" : "BBB" , "date" : 20160102}
document3 { "state" : "AAA" , "date" : 20160110}
... ...
documentN { "state" : "AAA" , "date" : 20161231}

其它的chunk大至致上也長的差不多,這時我們簡單的用表格來看看我們的cluster是什麼樣子,基本上最一開始chunk數量不多。

shard001 shard002 shard003
{ "AAA" ~ "BBB"} { "CCC" ~ DDD" } { "EEE" ~ "FFF" }

然後這時資料開始隨著時間流動,開始慢慢的增加,而當一個chunk裝不下時,就會開始拆開,這時我們的cluster大概是降。

shard001 shard002 shard003
{ "AAA" ~ "BBB"} ** { "CCC" ~ DDD" } ** { "EEE" ~ "FFF" } **
{ "AAA" ~ "BBB"} ** { "CCC" ~ DDD" }** { "EEE" ~ "FFF" } **
{ "AAA" ~ "BBB"} { "EEE" ~ "FFF" }

有沒有注意到我們上面有用**符號,這代表這個chunk不會在變動了,我們接下來的新增動作只會影響到最下面的chunk。這有什麼好處,好處是寫入請求時,非常的有效率,它只會去新增至最下面的chunk

這種策略另一種好處是搜尋,如果我們這時要尋找AAA的某段時間,它會處理的非常快,因為每一塊chunk時間區間都是固定的,而且如果要找最新的一定是去找最下面的chunk,因此如果某些需求是很常使用到搜尋某欄位(升序欄位)的範圍的很適和用這種策略。

~ 結語 ~

本篇文章花了很多的文字在慢慢的說明片鍵的選擇,片鍵真的是影響到你的分片的好壞,一個好的片鍵帶你上天堂,壞的片鍵帶你下地獄,沒選好真的會很好,尤其是那種很老的系統一開始片鍵就選錯,然後資料量又超大,如果有人叫你想辦法調整效能,你大概會選擇幹掉提出問題的人,記好,片鍵是要根據你的應用需求來決定,這很重要。

~參考資料~

← 30-24之MongoDB分片Sharding(2)---Chunk的札事 30-26之MongoDB運用研究---股價應用模擬(1) →
 
comments powered by Disqus