本文檔匯總了多篇文章知識的結(jié)晶,整理出一份完整的Redis集群搭建教程,在本文最后也有注明摘自原文的地址,如果原作者有意見,可與我聯(lián)系,侵刪。
Step 0 :集群概念
Redis 集群簡介
Redis 集群是一個可以在多個 Redis 節(jié)點之間進(jìn)行數(shù)據(jù)共享的設(shè)施(installation)。
Redis 集群不支持那些需要同時處理多個鍵的 Redis 命令, 因為執(zhí)行這些命令需要在多個 Redis 節(jié)點之間移動數(shù)據(jù), 并且在高負(fù)載的情況下, 這些命令將降低 Redis 集群的性能, 并導(dǎo)致不可預(yù)測的行為。
Redis 集群通過分區(qū)(partition)來提供一定程度的可用性(availability): 即使集群中有一部分節(jié)點失效或者無法進(jìn)行通訊, 集群也可以繼續(xù)處理命令請求。
Redis 集群提供了以下兩個好處:
- 將數(shù)據(jù)自動切分(split)到多個節(jié)點的能力。
- 當(dāng)集群中的一部分節(jié)點失效或者無法進(jìn)行通訊時, 仍然可以繼續(xù)處理命令請求的能力。
Redis 集群數(shù)據(jù)共享
Redis 集群使用數(shù)據(jù)分片(sharding)而非一致性哈希(consistency hashing)來實現(xiàn): 一個 Redis 集群包含 16384 個哈希槽(hash slot), 數(shù)據(jù)庫中的每個鍵都屬于這 16384 個哈希槽的其中一個, 集群使用公式 CRC16(key) % 16384 來計算鍵 key 屬于哪個槽, 其中 CRC16(key) 語句用于計算鍵 key 的 CRC16 校驗和。
集群中的每個節(jié)點負(fù)責(zé)處理一部分哈希槽。 舉個例子, 一個集群可以有三個哈希槽, 其中:
- 節(jié)點 A 負(fù)責(zé)處理
0號至5500號哈希槽。 - 節(jié)點 B 負(fù)責(zé)處理
5501號至11000號哈希槽。 - 節(jié)點 C 負(fù)責(zé)處理
11001號至16384號哈希槽。
這種將哈希槽分布到不同節(jié)點的做法使得用戶可以很容易地向集群中添加或者刪除節(jié)點。 比如說:
- 如果用戶將新節(jié)點 D 添加到集群中, 那么集群只需要將節(jié)點 A 、B 、 C 中的某些槽移動到節(jié)點 D 就可以了。
- 與此類似, 如果用戶要從集群中移除節(jié)點 A , 那么集群只需要將節(jié)點 A 中的所有哈希槽移動到節(jié)點 B 和節(jié)點 C , 然后再移除空白(不包含任何哈希槽)的節(jié)點 A 就可以了。
因為將一個哈希槽從一個節(jié)點移動到另一個節(jié)點不會造成節(jié)點阻塞, 所以無論是添加新節(jié)點還是移除已存在節(jié)點, 又或者改變某個節(jié)點包含的哈希槽數(shù)量, 都不會造成集群下線。
Redis 集群中的主從復(fù)制
為了使得集群在一部分節(jié)點下線或者無法與集群的大多數(shù)(majority)節(jié)點進(jìn)行通訊的情況下, 仍然可以正常運作, Redis 集群對節(jié)點使用了主從復(fù)制功能: 集群中的每個節(jié)點都有 1 個至 N 個復(fù)制品(replica), 其中一個復(fù)制品為主節(jié)點(master), 而其余的 N-1 個復(fù)制品為從節(jié)點(slave)。
在之前列舉的節(jié)點 A 、B 、C 的例子中, 如果節(jié)點 B 下線了, 那么集群將無法正常運行, 因為集群找不到節(jié)點來處理 5501 號至 11000 號的哈希槽。
另一方面, 假如在創(chuàng)建集群的時候(或者至少在節(jié)點 B 下線之前), 我們?yōu)橹鞴?jié)點 B 添加了從節(jié)點 B1 , 那么當(dāng)主節(jié)點 B 下線的時候, 集群就會將 B1 設(shè)置為新的主節(jié)點, 并讓它代替下線的主節(jié)點 B , 繼續(xù)處理 5501 號至 11000 號的哈希槽, 這樣集群就不會因為主節(jié)點 B 的下線而無法正常運作了。
不過如果節(jié)點 B 和 B1 都下線的話, Redis 集群還是會停止運作。
Redis 集群的一致性保證(guarantee)
Redis 集群不保證數(shù)據(jù)的強(qiáng)一致性(strong consistency): 在特定條件下, Redis 集群可能會丟失已經(jīng)被執(zhí)行過的寫命令。
使用異步復(fù)制(asynchronous replication)是 Redis 集群可能會丟失寫命令的其中一個原因。 考慮以下這個寫命令的例子:
- 客戶端向主節(jié)點 B 發(fā)送一條寫命令。
- 主節(jié)點 B 執(zhí)行寫命令,并向客戶端返回命令回復(fù)。
- 主節(jié)點 B 將剛剛執(zhí)行的寫命令復(fù)制給它的從節(jié)點 B1 、 B2 和 B3 。
如你所見, 主節(jié)點對命令的復(fù)制工作發(fā)生在返回命令回復(fù)之后, 因為如果每次處理命令請求都需要等待復(fù)制操作完成的話, 那么主節(jié)點處理命令請求的速度將極大地降低 —— 我們必須在性能和一致性之間做出權(quán)衡。
如果真的有必要的話, Redis 集群可能會在將來提供同步地(synchronou)執(zhí)行寫命令的方法。
Redis 集群另外一種可能會丟失命令的情況是, 集群出現(xiàn)網(wǎng)絡(luò)分裂(network partition), 并且一個客戶端與至少包括一個主節(jié)點在內(nèi)的少數(shù)(minority)實例被孤立。
舉個例子, 假設(shè)集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六個節(jié)點, 其中 A 、B 、C 為主節(jié)點, 而 A1 、B1 、C1 分別為三個主節(jié)點的從節(jié)點, 另外還有一個客戶端 Z1 。
假設(shè)集群中發(fā)生網(wǎng)絡(luò)分裂, 那么集群可能會分裂為兩方, 大多數(shù)(majority)的一方包含節(jié)點 A 、C 、A1 、B1 和 C1 , 而少數(shù)(minority)的一方則包含節(jié)點 B 和客戶端 Z1 。
在網(wǎng)絡(luò)分裂期間, 主節(jié)點 B 仍然會接受 Z1 發(fā)送的寫命令:
- 如果網(wǎng)絡(luò)分裂出現(xiàn)的時間很短, 那么集群會繼續(xù)正常運行;
- 但是, 如果網(wǎng)絡(luò)分裂出現(xiàn)的時間足夠長, 使得大多數(shù)一方將從節(jié)點 B1 設(shè)置為新的主節(jié)點, 并使用 B1 來代替原來的主節(jié)點 B , 那么 Z1 發(fā)送給主節(jié)點 B 的寫命令將丟失。
注意, 在網(wǎng)絡(luò)分裂出現(xiàn)期間, 客戶端 Z1 可以向主節(jié)點 B 發(fā)送寫命令的最大時間是有限制的, 這一時間限制稱為節(jié)點超時時間(node timeout), 是 Redis 集群的一個重要的配置選項:
- 對于大多數(shù)一方來說, 如果一個主節(jié)點未能在節(jié)點超時時間所設(shè)定的時限內(nèi)重新聯(lián)系上集群, 那么集群會將這個主節(jié)點視為下線, 并使用從節(jié)點來代替這個主節(jié)點繼續(xù)工作。
- 對于少數(shù)一方, 如果一個主節(jié)點未能在節(jié)點超時時間所設(shè)定的時限內(nèi)重新聯(lián)系上集群, 那么它將停止處理寫命令, 并向客戶端報告錯誤。