redis集群

無論是單機(jī)還是主從結(jié)構(gòu)蚁孔,都無法解決數(shù)據(jù)量過大導(dǎo)致內(nèi)存/虛擬內(nèi)存無法村下所有數(shù)據(jù)的問題问欠。redis集群通過分片根據(jù)key計算出數(shù)據(jù)所屬的槽(slot)唧席,通過指派使每個集群節(jié)點負(fù)責(zé)一部分槽內(nèi)數(shù)據(jù)的處理,從而成為一種分布式數(shù)據(jù)庫除嘹。

redis集群一共有16384個槽怎憋,集群內(nèi)每個節(jié)點可以負(fù)責(zé)處理其中一部分槽又碌。下圖表示集群內(nèi)有3個節(jié)點九昧,分別在端口7000,7001毕匀,7002铸鹰。其中0-5000號槽被指派給7000端口所在的節(jié)點,...


image.png

1.節(jié)點

1.1 構(gòu)建集群

redis集群的節(jié)點在初始時都是互相獨立的皂岔,要組建集群只需在某臺主機(jī)上使用CLUSTER MEET <ip> <port>命令蹋笼,當(dāng)前主機(jī)就會與目的主機(jī)進(jìn)行連接,連接成功后就會將對方添加到自己所在的集群內(nèi)凤薛。

為了使7000-7002端口所在的3個服務(wù)主機(jī)構(gòu)成一個[圖片上傳中...(image.png-891ba7-1632229388314-0)]
集群姓建,只需要在7000端口的客戶端分別輸入
CLUSTER MEET <127.0.0.1> <7001>
CLUSTER MEET <127.0.0.1> <7002>
即可。

image.png

1.2 集群對應(yīng)的數(shù)據(jù)結(jié)構(gòu)

集群內(nèi)每一個主機(jī)都對應(yīng)一個clusterNode類型的數(shù)據(jù)缤苫。

typedef struct clusterNode {
    mstime_t ctime; /* Node object creation time. */
    char name[CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */
    int flags;      /* CLUSTER_NODE_... */
  ...
    unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */
    int numslots;   /* Number of slots handled by this node */
    int numslaves;  /* Number of slave nodes, if this is a master */
    struct clusterNode **slaves; /* pointers to slave nodes */
    struct clusterNode *slaveof; /* pointer to the master node. Note that it
                                    may be NULL even if the node is a slave
                                    if we don't have the master node in our
                                    tables. */
  ...
    char ip[NET_IP_STR_LEN];  /* Latest known IP address of this node */
    int port;                   /* Latest known clients port of this node */
    int cport;                  /* Latest known cluster port of this node. */
    clusterLink *link;          /* TCP/IP link with this node */
  ...
} clusterNode;

clusterNode中比較關(guān)鍵的幾個屬性分別是: slots數(shù)組,長度為16384/8字節(jié)(16384 = 2^^14位墅拭,計算key應(yīng)位于哪個slot時取余數(shù)方便)活玲,以一個bitmap的形式表示了當(dāng)前節(jié)點處理的slot; 一個指向slave數(shù)組的指針(如果當(dāng)前主機(jī)為master)谍婉;一個指向master主機(jī)的指針(如果當(dāng)前為slave)舒憾;另外在clusterLink中包含了當(dāng)前節(jié)點的tcp/ip連接信息,如文件描述符穗熬,輸入镀迂、輸出緩沖區(qū)等。

整個集群的信息由clusterState表示唤蔗。 在每個節(jié)點中都會保存一個clusterState結(jié)構(gòu)探遵,表示在當(dāng)前主機(jī)的視角下集群所處的狀態(tài)。

typedef struct clusterState {
    // 指向表示當(dāng)前節(jié)點自身的clusterNode
    clusterNode *myself;  /* This node */
    uint64_t currentEpoch;
    // 集群當(dāng)前狀態(tài):在線 || 下線
    int state;
    // 集群中有多少節(jié)點至少處理一個slot
    int size;
    // 集群中所有節(jié)點的字典妓柜,以name : clusterNode形式存儲
    dict *nodes;          /* Hash table of name -> clusterNode structures */
  ...
    clusterNode *slots[CLUSTER_SLOTS];
  ...
} clusterState;

在上述3個節(jié)點創(chuàng)建完集群后的初始狀態(tài)下箱季,每個節(jié)點都沒有被分配任何負(fù)責(zé)處理的槽,在7000節(jié)點視角下狀態(tài)如下圖


image.png

1.3 槽指派

通過在某個節(jié)點的客戶端發(fā)送CLUSTER ADDSLOTS <slot> [slot ...]命令棍掐,可以將命令中的slot分派給當(dāng)前節(jié)點負(fù)責(zé)處理藏雏。分派主要需要完成這樣幾件事情:

  1. 記錄節(jié)點的槽指派信息。 即將節(jié)點對應(yīng)的clusterNode內(nèi)的slots數(shù)組相應(yīng)位置為1作煌, 同時使clusterState中的slots數(shù)組的對應(yīng)位存放的指針指向當(dāng)前clusterNode掘殴。
  2. 傳播節(jié)點的槽指派信息。當(dāng)前節(jié)點不僅要自己記錄一份指派信息粟誓,還需要將指派信息發(fā)送給其他節(jié)點奏寨。其他節(jié)點在收到后,修改自己的clusterState努酸,將slots數(shù)組的對應(yīng)位指向被分派的節(jié)點服爷,同時修改該節(jié)點的slots數(shù)組。

在所有節(jié)點指派完成之后,每個節(jié)點內(nèi)clusterState的slots數(shù)組狀態(tài)大致如下圖仍源。同時心褐,各個clusterNode內(nèi),slots數(shù)組中被該節(jié)點負(fù)責(zé)的槽被置位為1.


image.png

此時笼踩,想要知道某個slot被哪個節(jié)點負(fù)責(zé)處理逗爹,只需要通過clusterState的slots數(shù)組對應(yīng)位的指針,即可訪問到該節(jié)點的信息嚎于。而如果想要知道當(dāng)前節(jié)點所負(fù)責(zé)的slot信息(例如發(fā)送當(dāng)前節(jié)點負(fù)責(zé)處理的slot信息給其他節(jié)點)掘而,只需要取出myself指向的clusterNode,拿到它的slots數(shù)組于购,取出被置位1的位就行了袍睡。

2. 執(zhí)行命令

clusterState.slots可以快速定位到slot被哪個節(jié)點負(fù)責(zé)處理,因此在集群中執(zhí)行命令也就相對容易了:客戶端連接集群中任一節(jié)點肋僧,向其發(fā)送對key的處理命令斑胜。收到命令的節(jié)點現(xiàn)根據(jù)key計算出該key應(yīng)該配分配到哪個slot上,比如slot = CRC16(key)%16834根據(jù)clusterState.slots[slot]就能快速定位到對應(yīng)的節(jié)點N嫌吠。然后將節(jié)點N與myself指向的節(jié)點對比止潘,如果是同一個,那么就可以直接處理辫诅。如果不是凭戴,那么說明應(yīng)當(dāng)由其他節(jié)點處理。此時向客戶端返回MOVED錯誤炕矮,根據(jù)節(jié)點N的信息么夫,指引客戶端去請求對應(yīng)的主機(jī)。
其流程如下圖:

image.png

3.重新分片

重新分片可以將已經(jīng)指派給某節(jié)點的任意數(shù)量的槽吧享,重新分派給另外一個節(jié)點魏割。重新分派后,槽和槽所屬的鍵值對會被從源節(jié)點移動到目標(biāo)節(jié)點钢颂。重新分片可以在線進(jìn)行钞它,并且再重新分片過程中,源節(jié)點和目標(biāo)節(jié)點都可以繼續(xù)處理命令請求殊鞭。

重新分片由redis-trib負(fù)責(zé)執(zhí)行遭垛,通過向源節(jié)點和目標(biāo)節(jié)點發(fā)送命令來完成該操作。
對每個槽:

  1. 向目標(biāo)節(jié)點發(fā)送CLUSTER SETSLOT <slot> IMPORTING <source_id>命令操灿,讓其準(zhǔn)備好從源節(jié)點導(dǎo)入屬于slot的鍵值對數(shù)據(jù)
  2. 向源節(jié)點發(fā)送CLUSTER SETSLOT <slot> MIGRATING <target_id>命令锯仪,讓其準(zhǔn)備好將slot內(nèi)的鍵值對遷移到target
  3. 向源節(jié)點發(fā)送CLUSTER GETKEYSINSLOT <slot> <count>命令,獲得最多count個key name
  4. 對于每個key_name, redis-trib向源節(jié)點發(fā)送MIGRATE <target_ip> <target_port> <key_name> 0 <timeout>,將key原子地遷移至目標(biāo)節(jié)點
  5. 重復(fù)3和4趾盐,直到slot內(nèi)所有的key都被遷移完成庶喜。
  6. 向集群內(nèi)任一節(jié)點發(fā)送``CLUSTER SETSLOT <slot> NODE <target_id>```小腊,將slot指派給target,然后通過消息發(fā)送到整個集群久窟,每個節(jié)點更新自己的slotState秩冈。


    遷移slot內(nèi)的鍵

4.復(fù)制與故障轉(zhuǎn)移

1.集群設(shè)置從節(jié)點

向某個服務(wù)器發(fā)送 CLUSTER REPLICATE <node_id>命令,使之復(fù)制node_id對應(yīng)的節(jié)點斥扛,成為其從節(jié)點入问。收到命令的服務(wù)器需要做以下幾件事:

  1. 在自己的clusterState.nodes中找到node_id對應(yīng)的clusterNode,將自己的slaveof指向該node稀颁。
  2. 修改自身flag芬失, 由master 變?yōu)閟lave
    3.調(diào)用復(fù)制代碼,根據(jù)master的ip:port發(fā)起復(fù)制請求匾灶。(這一過程和之前講的主從復(fù)制相同棱烂,類似于SLAVEOF <master_ip> <master_port>)
  3. 通知集群內(nèi)其他節(jié)點,這些節(jié)點收到通知后修改自己的clusterState

2.故障檢測

  • 集群內(nèi)每個節(jié)點定期向集群內(nèi)其他節(jié)點發(fā)送PING消息阶女,如果某節(jié)點回復(fù)超時垢啼,那么發(fā)送的節(jié)點將其標(biāo)記為疑似下線。(修改clusterState.nodes中對于clusterNode的標(biāo)志)
  • 各節(jié)點間通過消息交換集群中各節(jié)點的狀態(tài)信息张肾。
  • 當(dāng)某節(jié)點A得知B認(rèn)為C疑似下線時,向C對應(yīng)的clusterNode.fail_reports添加該報告锚扎。
    如果集群內(nèi)半數(shù)以上負(fù)責(zé)處理槽的主節(jié)點都向節(jié)點A報告C為疑似下線吞瞪,那么X就被A標(biāo)記為已下線。A將C已下線的消息廣播到集群內(nèi)驾孔,所有收到消息的節(jié)點都將該節(jié)點標(biāo)記為已下線芍秆。

3.故障轉(zhuǎn)移

當(dāng)已下線節(jié)點C的從節(jié)點C1收到C已下線時,開始進(jìn)行故障轉(zhuǎn)移翠勉。

  1. 選舉新的主節(jié)點妖啥。 (基于raft領(lǐng)導(dǎo)選舉算法)
    1.1 某個從節(jié)點開始故障轉(zhuǎn)移時,先將自己內(nèi)部clusterState.epoch加1对碌,然后發(fā)起投票
    1.2 負(fù)責(zé)處理槽的主節(jié)點具有投票權(quán)荆虱,每個投票者根據(jù)發(fā)過來的epoch和自己當(dāng)前的epoch對比,如果當(dāng)前epoch小于請求的epoch朽们,則投票給他怀读,并更新自己的epoch。
    1.3 勝出規(guī)則: 得票數(shù)不小于 N/2 +1 則當(dāng)選為新的主節(jié)點骑脱。
    1.4 如果沒人勝出菜枷,則進(jìn)行下一輪投票
  2. 將已下線主節(jié)點負(fù)責(zé)處理的槽指派給自己。
  3. 將自己成為主節(jié)點的消息廣播給集群內(nèi)的其他節(jié)點叁丧。
  4. 轉(zhuǎn)移完成啤誊,C1開始處理落在原來由C負(fù)責(zé)的槽內(nèi)的請求岳瞭。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蚊锹,隨后出現(xiàn)的幾起案子瞳筏,更是在濱河造成了極大的恐慌,老刑警劉巖枫耳,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乏矾,死亡現(xiàn)場離奇詭異,居然都是意外死亡迁杨,警方通過查閱死者的電腦和手機(jī)钻心,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铅协,“玉大人捷沸,你說我怎么就攤上這事『罚” “怎么了痒给?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長骏全。 經(jīng)常有香客問我苍柏,道長,這世上最難降的妖魔是什么姜贡? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任试吁,我火速辦了婚禮,結(jié)果婚禮上楼咳,老公的妹妹穿的比我還像新娘熄捍。我一直安慰自己,他們只是感情好母怜,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布余耽。 她就那樣靜靜地躺著,像睡著了一般苹熏。 火紅的嫁衣襯著肌膚如雪碟贾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天柜裸,我揣著相機(jī)與錄音缕陕,去河邊找鬼。 笑死疙挺,一個胖子當(dāng)著我的面吹牛扛邑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铐然,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蔬崩,長吁一口氣:“原來是場噩夢啊……” “哼恶座!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沥阳,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤跨琳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后桐罕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脉让,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年功炮,在試婚紗的時候發(fā)現(xiàn)自己被綠了溅潜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡薪伏,死狀恐怖滚澜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嫁怀,我是刑警寧澤设捐,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站塘淑,受9級特大地震影響萝招,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜存捺,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一即寒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧召噩,春花似錦、人聲如沸逸爵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽师倔。三九已至构韵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間趋艘,已是汗流浹背疲恢。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留瓷胧,地道東北人显拳。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像搓萧,于是被迫代替她去往敵國和親杂数。 傳聞我的和親對象是個殘疾皇子宛畦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 在對數(shù)據(jù)庫中的16384個槽都進(jìn)行了指派之后,集群就會進(jìn)入上線狀態(tài)揍移,這時客戶端就可以向集群中的節(jié)點發(fā)送數(shù)據(jù)命令了次和。...
    c84f3109853b閱讀 674評論 0 0
  • redis集群是redis提供分布式數(shù)據(jù)庫方案,集群通過分片來進(jìn)行數(shù)據(jù)共享那伐,并提供復(fù)制和故障轉(zhuǎn)移功能 節(jié)點 集群有...
    有何不可12317閱讀 258評論 0 0
  • 一踏施、數(shù)據(jù)分布 1. Redis數(shù)據(jù)分區(qū) Redis Cluser采用虛擬槽分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到0~16...
    handsomemao666閱讀 846評論 0 1
  • 1. Redis 集群 1.1 集群節(jié)點 加入集群:節(jié)點使用cluster meet 命令來嘗試握手, 若成...
    滬上最強(qiáng)亞巴頓閱讀 575評論 0 0
  • redis集群是redis提供的分布式方案,集群通過分片(sharding)來進(jìn)行數(shù)據(jù)共享并提供復(fù)制和故障轉(zhuǎn)移. ...
    王小帥_94閱讀 199評論 0 0