本篇文章給大家分享的是有關(guān)redis中Cluster分區(qū)的實(shí)現(xiàn)原理是什么,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
10年的克拉瑪依網(wǎng)站建設(shè)經(jīng)驗(yàn),針對設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營銷型網(wǎng)站建設(shè)的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整克拉瑪依建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“克拉瑪依網(wǎng)站設(shè)計(jì)”,“克拉瑪依網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
Redis Cluster本身提供了自動將數(shù)據(jù)分散到Redis Cluster不同節(jié)點(diǎn)的能力,分區(qū)實(shí)現(xiàn)的關(guān)鍵點(diǎn)問題包括:如何將數(shù)據(jù)自動地打散到不同的節(jié)點(diǎn),使得不同節(jié)點(diǎn)的存儲數(shù)據(jù)相對均勻;如何保證客戶端能夠訪問到正確的節(jié)點(diǎn)和數(shù)據(jù);如何保證重新分片的過程中不影響正常服務(wù)。這篇文章通過了解這些問題來認(rèn)識Redis Cluster分區(qū)實(shí)現(xiàn)原理。
Redis Cluster是由多個(gè)同時(shí)服務(wù)于一個(gè)數(shù)據(jù)集合的Redis實(shí)例組成的整體,對于用戶來說,用戶只關(guān)注這個(gè)數(shù)據(jù)集合,而整個(gè)數(shù)據(jù)集合的某個(gè)數(shù)據(jù)子集存儲在哪個(gè)節(jié)點(diǎn)對于用戶來說是透明的。Redis Cluster具有分布式系統(tǒng)的特點(diǎn),也具有分布式系統(tǒng)如何實(shí)現(xiàn)高可用性與數(shù)據(jù)一致性的難點(diǎn),由多個(gè)Redis實(shí)例組成的Redis Cluster結(jié)構(gòu)通常如下:
Redis Cluster
Redis Cluster特點(diǎn)如下:
所有的節(jié)點(diǎn)相互連接;
集群消息通信通過集群總線通信,,集群總線端口大小為客戶端服務(wù)端口+10000,這個(gè)10000是固定值;
節(jié)點(diǎn)與節(jié)點(diǎn)之間通過二進(jìn)制協(xié)議進(jìn)行通信;
客戶端和集群節(jié)點(diǎn)之間通信和通常一樣,通過文本協(xié)議進(jìn)行;
集群節(jié)點(diǎn)不會代理查詢;
Redis Cluster中有一個(gè)16384長度的槽的概念,他們的編號為0、1、2、3……16382、16383。這個(gè)槽是一個(gè)虛擬的槽,并不是真正存在的。正常工作的時(shí)候,Redis Cluster中的每個(gè)Master節(jié)點(diǎn)都會負(fù)責(zé)一部分的槽,當(dāng)有某個(gè)key被映射到某個(gè)Master負(fù)責(zé)的槽,那么這個(gè)Master負(fù)責(zé)為這個(gè)key提供服務(wù),至于哪個(gè)Master節(jié)點(diǎn)負(fù)責(zé)哪個(gè)槽,這是可以由用戶指定的,也可以在初始化的時(shí)候自動生成(redis-trib.rb腳本)。這里值得一提的是,在Redis Cluster中,只有Master才擁有槽的所有權(quán),如果是某個(gè)Master的slave,這個(gè)slave只負(fù)責(zé)槽的使用,但是沒有所有權(quán)。Redis Cluster怎么知道哪些槽是由哪些節(jié)點(diǎn)負(fù)責(zé)的呢?某個(gè)Master又怎么知道某個(gè)槽自己是不是擁有呢?
Master節(jié)點(diǎn)維護(hù)著一個(gè)16384/8字節(jié)的位序列,Master節(jié)點(diǎn)用bit來標(biāo)識對于某個(gè)槽自己是否擁有。比如對于編號為1的槽,Master只要判斷序列的第二位(索引從0開始)是不是為1即可。
位序列
如上面的序列,表示當(dāng)前Master擁有編號為1,134的槽。集群同時(shí)還維護(hù)著槽到集群節(jié)點(diǎn)的映射,是由長度為16384類型為節(jié)點(diǎn)的數(shù)組實(shí)現(xiàn)的,槽編號為數(shù)組的下標(biāo),數(shù)組內(nèi)容為集群節(jié)點(diǎn),這樣就可以很快地通過槽編號找到負(fù)責(zé)這個(gè)槽的節(jié)點(diǎn)。位序列這個(gè)結(jié)構(gòu)很精巧,即不浪費(fèi)存儲空間,操作起來又很便捷。
這里講的是Redis Cluster如何將鍵空間分布在不同的節(jié)點(diǎn)的,鍵空間意為Redis Cluster所擁有用戶所有數(shù)據(jù)集合的鍵的取值范圍,這個(gè)范圍叫做鍵空間。提到空間分布,必然會想到哈希算法,沒錯(cuò),通過哈希算法再加上取模運(yùn)算可以將一個(gè)值固定地映射到某個(gè)區(qū)間,在這里,這個(gè)區(qū)間叫做slots,區(qū)間由連續(xù)的slot組成。在Redis Cluster中,我們擁有16384個(gè)slot,這個(gè)數(shù)是固定的,我們存儲在Redis Cluster中的所有的鍵都會被映射到這些slot中,下面講講Redis Cluster是如何做映射的。
鍵到slot的基本映射算法如下:
HASH_SLOT = CRC16(key) mod 16384
用Redis中的代碼表示如下(這個(gè)代碼被稍微修改了一下,后面會還原):
crc16(key) & 0x3FFF
經(jīng)過簡單的計(jì)算就得到了當(dāng)前key應(yīng)該是存儲在哪個(gè)slot里面,值得注意的是,指定的key會被存儲在哪個(gè)slot,這個(gè)關(guān)系是鐵打不變的。如果我提交了一批命令,往Redis中存儲一批鍵,那么這些鍵一般會被映射到不同的slot,而不同的slot又可能由Redis Cluster中不同的節(jié)點(diǎn)服務(wù),這樣就和的預(yù)期有點(diǎn)不同,有沒有辦法將這批鍵映射到同一個(gè)slot呢?答案是可以。
鍵哈希標(biāo)簽是一種可以讓用戶指定將一批鍵都能夠被存放在同一個(gè)槽中的實(shí)現(xiàn)方法,用戶唯一要做的就是按照既定規(guī)則生成key即可,這個(gè)規(guī)則是這樣的,如果我有對于同一個(gè)用戶有兩種不同含義的兩份數(shù)據(jù),我只要將他們的鍵設(shè)置為下面即可:
abc{userId}def和ghi{userId}jkl
redis在計(jì)算槽編號的時(shí)候只會獲取{}之間的字符串進(jìn)行槽編號計(jì)算,這樣由于上面兩個(gè)不同的鍵,{}里面的字符串是相同的,因此他們可以被計(jì)算出相同的槽,相關(guān)代碼如下:
unsigned int keyHashSlot(char *key, int keylen) {
int s, e;
for (s = 0; s < keylen; s++)
if (key[s] == '{') break;
if (s == keylen) return crc16(key,keylen) & 0x3FFF;
for (e = s+1; e < keylen; e++)
if (key[e] == '}') break;
if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
return crc16(key+s+1,e-s-1) & 0x3FFF;
}
客戶端是怎么在Redis Cluster中找到正確的節(jié)點(diǎn)的呢?下面看看。
文章開始講到,Redis Cluster并不會代理查詢,那么如果客戶端訪問了一個(gè)key并不存在的節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)是怎么處理的呢?比如我想獲取key為msg的值,msg計(jì)算出來的槽編號為254,當(dāng)前節(jié)點(diǎn)正好不負(fù)責(zé)編號為254的槽,那么就會返回客戶端下面信息:
GET msg
-MOVED 254 127.0.0.1:6381
表示客戶端想要的254槽由運(yùn)行在IP為127.0.0.1,端口為6381的Master實(shí)例服務(wù)。如果根據(jù)key計(jì)算得出的槽恰好由當(dāng)前節(jié)點(diǎn)負(fù)責(zé),則當(dāng)期節(jié)點(diǎn)會立即返回結(jié)果。這里明確一下,沒有代理的Redis Cluster可能會導(dǎo)致客戶端兩次連接急群中的節(jié)點(diǎn)才能找到正確的服務(wù),推薦客戶端緩存連接,這樣最壞的情況是兩次往返通信。
重新分片意為槽到集群節(jié)點(diǎn)的映射關(guān)系要改變,不變的是鍵到槽的映射關(guān)系,因此當(dāng)重新分片的時(shí)候,如果槽中有鍵,那么鍵也是要被移動到新的節(jié)點(diǎn)的。下面看看重新分片是怎么做的,假如我們有一批槽需要從一個(gè)Master節(jié)點(diǎn)移動到另一個(gè)Master節(jié)點(diǎn):
槽遷移示意圖
這里簡化模型,假設(shè)這批待遷移的槽編號為1、2、3,并假設(shè)左邊的節(jié)點(diǎn)為MasterA節(jié)點(diǎn),右邊的節(jié)點(diǎn)為MasterB節(jié)點(diǎn)。
槽遷移的過程中有一個(gè)不穩(wěn)定狀態(tài),這個(gè)不穩(wěn)定狀態(tài)會有一些規(guī)則,這些規(guī)則定義客戶端的行為,從而使得Redis Cluster不必宕機(jī)的情況下可以執(zhí)行槽的遷移。下面這張圖描述了我們遷移編號為1、2、3的槽的過程中,他們在MasterA節(jié)點(diǎn)和Master節(jié)點(diǎn)中的狀態(tài)。槽1、2、3在MasterA節(jié)點(diǎn)中的狀態(tài)為MIGRATING,在MasterB節(jié)點(diǎn)中的狀態(tài)為IMPORTING。
槽遷移中間狀態(tài)
本例中MIGRATING狀態(tài)是發(fā)生在MasterA節(jié)點(diǎn)中的一種槽的狀態(tài),預(yù)備遷移槽的時(shí)候槽的狀態(tài)首先會變?yōu)镸IGRATING狀態(tài),這種狀態(tài)的槽會實(shí)際產(chǎn)生什么影響呢?當(dāng)客戶端請求的某個(gè)Key所屬的槽處于MIGRATING狀態(tài)的時(shí)候,影響有下面幾條:
如果Key存在則成功處理
如果Key不存在,則返回客戶端ASK,僅當(dāng)這次請求會轉(zhuǎn)向另一個(gè)節(jié)點(diǎn),并不會刷新客戶端中node的映射關(guān)系,也就是說下次該客戶端請求該Key的時(shí)候,還會選擇MasterA節(jié)點(diǎn)
如果Key包含多個(gè)命令,如果都存在則成功處理,如果都不存在,則返回客戶端ASK,如果一部分存在,則返回客戶端TRYAGAIN,通知客戶端稍后重試,這樣當(dāng)所有的Key都遷移完畢的時(shí)候客戶端重試請求的時(shí)候回得到ASK,然后經(jīng)過一次重定向就可以獲取這批鍵
本例中的IMPORTING狀態(tài)是發(fā)生在MasterB節(jié)點(diǎn)中的一種槽的狀態(tài),預(yù)備將槽從MasterA節(jié)點(diǎn)遷移到MasterB節(jié)點(diǎn)的時(shí)候,槽的狀態(tài)會首先變?yōu)镮MPORTING。IMPORTING狀態(tài)的槽對客戶端的行為有下面一些影響:
正常命令會被MOVED重定向,如果是ASKING命令則命令會被執(zhí)行,從而Key沒有在老的節(jié)點(diǎn)已經(jīng)被遷移到新的節(jié)點(diǎn)的情況可以被順利處理;
如果Key不存在則新建;
沒有ASKING的請求和正常請求一樣被MOVED,這保證客戶端node映射關(guān)
系出錯(cuò)的情況下不會發(fā)生寫錯(cuò);
鍵空間遷移是指當(dāng)滿足了槽遷移前提的情況下,我們就可以通過相關(guān)命令將槽1、2、3中的鍵空間從MasterA節(jié)點(diǎn)轉(zhuǎn)移到MasterB節(jié)點(diǎn),這個(gè)過程真正實(shí)現(xiàn)數(shù)據(jù)轉(zhuǎn)移。相關(guān)命令:
MIGRATE
DUMP
RESTORE
DEL
以上就是Redis中Cluster分區(qū)的實(shí)現(xiàn)原理是什么,小編相信有部分知識點(diǎn)可能是我們?nèi)粘9ぷ鲿姷交蛴玫降摹OM隳芡ㄟ^這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。