????分布式數據庫首要問題是要解決把整個數據集按照分區(qū)的規(guī)則映射到多個節(jié)點的問題。即把數據劃分到多個節(jié)點上超歌,每個節(jié)點負責整體數據的一個子集。大致如下圖所示:
我們需要重點關注的是分區(qū)的規(guī)則脆荷。常見的規(guī)則由順序分區(qū)懊悯,哈希分區(qū)定枷。
順序分區(qū):離散度易傾斜,數據分布業(yè)務相關覆旭,可順序訪問岖妄。
哈希分區(qū):離散度好,數據分布業(yè)務無關七兜,無法順序訪問福扬。
因為redis cluster采用了哈希分區(qū)規(guī)則,我們就重點介紹哈希分區(qū)狠裹。常見的哈希分區(qū)規(guī)則有幾種:
節(jié)點取余分區(qū):
使用特定的數據(包括redis的鍵或用戶ID)汽烦,再根據節(jié)點數量N,使用公式:hash(key)%N計算出一個0~(N-1)值俗冻,用來決定數據映射到哪一個節(jié)點上牍颈。即哈希值對節(jié)點總數取余煮岁。余數x死姚,表示這條數據存放在第(x+1)個節(jié)點上勤篮。
優(yōu)點:簡單
缺點:當節(jié)點數量N變化時(擴容或者收縮),數據和節(jié)點之間的映射關系需要重新計算账劲,這樣的話金抡,按照新的規(guī)則映射梗肝,要么之前存儲的數據找不到,要么之前數據被重新映射到新的節(jié)點(導致以前存儲的數據發(fā)生數據遷移)禀晓。這是難以接受的坝锰。
實踐:常用于數據庫的分庫分表規(guī)則,一般采用預分區(qū)的方式凫乖,提前根據數據量規(guī)劃好分區(qū)數弓颈,比如劃分為512或1024張表翔冀,保證可支撐未來一段時間的數據量,再根據負載情況將表遷移到其他數據庫中菊匿。擴容時通常采用翻倍擴容计福,避免數據映射全部被打亂導致全量遷移的情況徽职,如下圖所示:
一致性哈希分區(qū):
在一致性哈希中姆钉,我們把所有的數據包括節(jié)點數據都放在一個哈希環(huán)上抄瓦,我們除了需要計算要存儲的數據的key的hash之外陶冷,還要計算節(jié)點的hash埂伦,然后在存儲時,選擇一個跟key的hash最接近的節(jié)點(順時針找到第一個大于等于該哈希值的節(jié)點)膊毁,存儲進去.
優(yōu)點:加入和刪除節(jié)點只影響哈希環(huán)中相鄰的節(jié)點基跑,對其他的節(jié)點無影響。
缺點:1加減節(jié)點栅螟,會造成哈希環(huán)中部分數據無法命中逆日,需要手動處理或者忽略這部分數據室抽,因此一致性哈希常用于緩存場景。晓折?兽泄?漓概?2當使用少量節(jié)點時,節(jié)點變化將大范圍影響哈希環(huán)中數據映射病梢,因此這種方式不適合少量數據節(jié)點的分布式方案胃珍。3普通的一致性哈希分區(qū)在增加節(jié)點時需要增加一倍或減去一半節(jié)點才能保證數據和負載的均衡。
上圖中的前綴為s的節(jié)點蜓陌,表示集群中的一個節(jié)點觅彰,前綴為x的節(jié)點,表示要寫或讀的數據.從第一張圓圈中钮热,我們可以看到填抬,由于x1, x2的hash值里s0最近,所以被存到s0這臺節(jié)點中了隧期,同理赘娄,x0被存到了s2這個節(jié)點中了.在第二個圓圈中,由于多了一臺節(jié)點宏蛉,s3, 而x2現在又離它最近遣臼,所以,x2需要從s0節(jié)點遷移到s3中.
從上圖中拾并,我們也能看到暑诸,一致性哈希解決了由于集群變動而導致的數據遷移率高的問題。但是又引入了另一個復雜的問題辟灰,就是每個節(jié)點的負載不相同个榕,因為每個節(jié)點的hash是根據IP計算出來的.換句話說就是:假設key足夠多,被hash算法打散得非常均勻芥喇,但是由于圖中的S沒有被均勻的打散在環(huán)形中西采,導致每個節(jié)點處理的key個數不太一樣,甚至相差很大继控。
虛擬槽分區(qū):
虛擬槽分區(qū)就是為了解決一致性哈希分區(qū)的不足而被創(chuàng)造的械馆。虛擬槽分區(qū)巧妙地使用了哈希空間武通,使用分散度良好的哈希函數把所有的數據映射到一個固定范圍的整數集合中霹崎,整數定義為槽(slot)。這個范圍一般遠遠大于節(jié)點數冶忱,比如redis Cluster槽的范圍是0-16383尾菇,一共16384個槽。槽是集群內數據管理和遷移的基本單位囚枪。采用大范圍槽的主要目的是為了方便數據拆分和集群擴展派诬。每個節(jié)點會負責一定數量的槽(虛擬節(jié)點),之前我們在介紹一致性哈希的時候链沼,是將物理節(jié)點直接通過哈希運算得到其hash值默赂,而后數據的key計算出來之后,與節(jié)點的哈希進行比較括勺,決定存放在哪個節(jié)點中.而現在缆八,我們用幾個槽(虛擬節(jié)點)代表一個物理節(jié)點.不同槽(虛擬節(jié)點)的hash是通過不同的哈希函數計算出來的。以redis為例子疾捍,假設現在我們有5個節(jié)點奈辰,一共分配16384個槽(虛擬節(jié)點)。那么數據拾氓,槽(虛擬節(jié)點)冯挎,節(jié)點的關系看起來就是這樣的了:
redis把數據分布到不同節(jié)點帶來集群功能的限制:
1>key批量操作支持有限:如mset底哥,mget,目前只支持 具有相同slot值得key執(zhí)行批量操作咙鞍。對于映射為不同slot值得key由于執(zhí)行mset,mget等操作可能存在于多個節(jié)點上因此不被支持房官。
2>key事物操作支持有限:同理只支持多個key在同一個節(jié)點上的事物操作,當多個key分布在不同的節(jié)點上時無法使用事物功能续滋。
3>key作為數據分區(qū)的最小粒度翰守,因此不能將一個大的鍵值對象如hash,list等映射到不同的節(jié)點。
4>不支持多數據庫空間疲酌。單機下的redis可以支持16個數據庫蜡峰,集群模式下只能使用一個數據空間,即db 0朗恳。
5>復制結構只支持一層湿颅,從節(jié)點只能復制主節(jié)點,不支持嵌套樹狀復制結構粥诫。