Redis部署手記之集羣模式

1.1簡介
Redis 在 3.0 版本後開始支持集羣模式(Redis Cluster),目前官方一直都在維護中,具有代
表性,建議優先使用。
Redis Cluster 是一種服務器 Sharding 技術,只要將查詢請求發送到 Redis Cluster 中的任意節
點,接收到請求的節點會就將查詢請求發送到正確的節點上執行:
當 Redis 客戶端操作的 key 恰好在所查詢的 Redis 節點上時,就像操作 Redis 單例一樣。
當客戶端操作的 key 沒有分配到所查詢的 Redis 節點上時,Redis 會返回轉向指令,指向正確的Redis 節點,這類似於瀏覽器頁面的 302 重定向跳轉。

Redis Cluster 並沒有使用一致性 Hash,而是採用 slot(槽)的概念,一共分成 16384 個槽。
Redis Cluster 要保證 16384 個槽對應的 Redis 節點都正常工作,否則一旦某個 Redis 節點發生故障,那它負責的 slots 也就失效,整個集羣將不能工作。
爲了增加集羣的高可用性,官方推薦的方案是將 Redis 節點配置成主從結構,即一個主節點,掛 N 個從節點。這時,如果主節點失效,Redis Cluster 會根據選舉算法在從節點中選擇一個上升爲主節點,整個集羣繼續對外提供服務。

Redis Cluster 的架構模式如下圖:

在這裏插入圖片描述
在這裏插入圖片描述
上圖描述的是六個 redis 實例構成的集羣(三主三從),其中:

  • 6379 端口爲客戶端通訊端口
  • 16379 端口爲集羣總線端口
  • 集羣內部劃分爲 16384 個數據分槽,分佈在三個主節點中
  • 從節點沒有分槽,不會參與集羣投票,也不會提供數據讀取服務,僅作爲主節點的備份
  • 三個主節點中平均分佈着 16384 數據分槽的三分之一,每個節點中不會存有有重複數據,僅僅使用自己的從機幫忙冗餘

Redis Cluster 具有以下優點:

  • 無中心架構,支持動態擴容,對業務透明
  • 具備 Sentinel 的監控和自動故障遷移能力
  • 高性能,客戶端直連 Redis 服務,免去了 Proxy 代理的損耗
  • 客戶端不需要連接集羣所有節點,連接集羣中任何一個可用節點即可(實際上是必須連接整個集,避免單點故障導致客戶端不可用)

Redis Cluster 同時也具有以下缺點:

  • 部署和運維複雜
  • 數據遷移需要人工干預
  • 只能使用 0 號數據庫
  • 不支持批量操作
  • 分佈式邏輯和存儲模塊耦合

1.2部署
下面署集羣模式,官方推薦 Redis Cluster 至少需要六個節點,即三主三從。
這裏準備六個 Redis 節點,同時爲了區分主從/哨兵模式的部署位置,順便拷貝一下 Redis 集羣節點的目錄(以節點端口號區分):

#mkdir /usr/local/redis-cluster            # 創建 Redis 集羣目錄
#cp /usr/local/redis-4.0.11/src/redis-trib.rb /usr/local/redis-cluster/   # 集羣構建腳本
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6390     # Redis 集羣節點目錄
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6391     # Redis 集羣節點目錄
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6392     # Redis 集羣節點目錄
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6393     # Redis 集羣節點目錄
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6394     # Redis 集羣節點目錄
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6395     # Redis 集羣節點目錄

接下來會基於/usr/local/redis-cluster/redis-xxxx目錄部署集羣模式。
然後配置這六個 Redis 節點的配置文件,其配置基本相同(主從無需配置,在後面初次構建集羣時會自動分配,而以後添加新節點,可人工指定是主節點還是從節點):

角色 配置文件 服務端口
集羣節點 /usr/local/redis-cluster/redis-6390/redis.conf 6390
集羣節點 /usr/local/redis-cluster/redis-6391/redis.conf 6391
集羣節點 /usr/local/redis-cluster/redis-6392/redis.conf 6392
集羣節點 /usr/local/redis-cluster/redis-6393/redis.conf 6393
集羣節點 /usr/local/redis-cluster/redis-6394/redis.conf 6394
集羣節點 /usr/local/redis-cluster/redis-6395/redis.conf 6395

配置文件/usr/local/redis-cluster/redis-639x/redis.conf的內容如下(僅端口號不同):

bind 127.0.0.1                  # 正式部署請設爲合適的 IP
port 639x
daemonize yes
pidfile /var/run/redis_639x.pid
dir /tmp/redis-cluster          # Redis 的工作目錄(若不存在需手建否則無法啓動),logfile 與 dbfilename 受其影響
logfile "639x.log"              # Redis 日誌名稱(默認不配置,表示輸出到 stdout),正式部署請設置爲合適的名稱
dbfilename dump-639x.rdb        # Redis 數據持久化時的存儲位置,正式部署請設置爲合適的名稱
cluster-enabled yes             # 啓用集羣模式
cluster-config-file nodes-639x.conf    # 集羣節點的配置文件,由集羣創建,但若同一臺主機上的名稱需唯一
cluster-node-timeout 15000

新建上面配置的 Redis 集羣工作目錄:

#mkdir /tmp/redis-cluster

然後使用 redis-server命令啓動六個 Redis 節點:

#cd /usr/local/redis-cluster/redis-6390/src/     # 切換到 Redis-6390 節點的啓動腳本目錄
#./redis-server ../redis.conf                    # 啓動 Redis 節點


#cd /usr/local/redis-cluster/redis-6391/src/
#./redis-server ../redis.conf


#cd /usr/local/redis-cluster/redis-6392/src/
#./redis-server ../redis.conf


#cd /usr/local/redis-cluster/redis-6393/src/
#./redis-server ../redis.conf


cd /usr/local/redis-cluster/redis-6394/src/
./redis-server ../redis.conf


#cd /usr/local/redis-cluster/redis-6395/src/
#./redis-server ../redis.conf

先通過 ps -ef|grep redis命令可查看六個節點進程是否正常啓動:
在這裏插入圖片描述
然後使用 Redis 官方提供的工具redis-trib.rb(注意:這個是 ruby 腳本,需要安裝相關支持庫,否則無法運行)把這 6 個節點組建成集羣(注意:若集羣節點分佈在多臺不同的機器上,只需其中一個機器執行這條命令即可,但要包含所有機器的集羣節點):

#cd /usr/local/redis-cluster/     # 切換到集羣構建腳本目錄
#./redis-trib.rb create --replicas 1 127.0.0.1:6390 127.0.0.1:6391 127.0.0.1:6392 127.0.0.1:6393 127.0.0.1:6394 127.0.0.1:6395

注:若以後機器 IP 發生變更,需要重新執行此命令重建集羣(重建集羣需要先停止集羣中所有節點進程,然後刪除原集羣中所有節點的 node.conf 文件,最後按照上文步驟構建集羣即可)。

此時 Redis 會返回正在構建的集羣信息,返回的信息大概意思是“正在把 slots 槽位分配到 6個節點的集羣,其中 6390、6391、6392 是主節點,6394 是 6390 的從節點,6395 是 6391 的從節點,6393 是 6392 的從節點”。
這裏的主從分配是自動的,若確認無誤則輸入 yes:

[root@localhost redis-cluster]# ./redis-trib.rb create --replicas 1 127.0.0.1:6390 127.0.0.1:6391 127.0.0.1:6392 127.0.0.1:6393 127.0.0.1:6394 127.0.0.1:6395
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:        #3個主節點
127.0.0.1:6390
127.0.0.1:6391
127.0.0.1:6392
Adding replica 127.0.0.1:6394 to 127.0.0.1:6390    #3個從節點
Adding replica 127.0.0.1:6395 to 127.0.0.1:6391
Adding replica 127.0.0.1:6393 to 127.0.0.1:6392
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: c736fda2d0b86917b26ae6cbf1d2a100dfdf9b1d 127.0.0.1:6390
   slots:0-5460 (5461 slots) master
M: a81920b65349ca9e49b4ba02cc276f7e7fffba94 127.0.0.1:6391
   slots:5461-10922 (5462 slots) master
M: 4795c10b891f214c147f779b222745ad71231340 127.0.0.1:6392
   slots:10923-16383 (5461 slots) master
S: cfb902bc8c6ed5bd548cc9aadcaea124944eff1a 127.0.0.1:6393
   replicates a81920b65349ca9e49b4ba02cc276f7e7fffba94
S: 15bbc002ac6103dfdcc61fba24f42aaf84a306ad 127.0.0.1:6394
   replicates 4795c10b891f214c147f779b222745ad71231340
S: 38da95e8ccf14b6b6990398c32fef39b68e7b3fe 127.0.0.1:6395
   replicates c736fda2d0b86917b26ae6cbf1d2a100dfdf9b1d
Can I set the above configuration? (type 'yes' to accept): yes      # 若確認無誤則輸入 yes

若構建集羣成功,則會返回集羣內這 6 個節點的信息:
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join....
>>> Performing Cluster Check (using node 127.0.0.1:6390)
M: c736fda2d0b86917b26ae6cbf1d2a100dfdf9b1d 127.0.0.1:6390
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: cfb902bc8c6ed5bd548cc9aadcaea124944eff1a 127.0.0.1:6393
   slots: (0 slots) slave
   replicates a81920b65349ca9e49b4ba02cc276f7e7fffba94
M: 4795c10b891f214c147f779b222745ad71231340 127.0.0.1:6392
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 15bbc002ac6103dfdcc61fba24f42aaf84a306ad 127.0.0.1:6394
   slots: (0 slots) slave
   replicates 4795c10b891f214c147f779b222745ad71231340
S: 38da95e8ccf14b6b6990398c32fef39b68e7b3fe 127.0.0.1:6395
   slots: (0 slots) slave
   replicates c736fda2d0b86917b26ae6cbf1d2a100dfdf9b1d
M: a81920b65349ca9e49b4ba02cc276f7e7fffba94 127.0.0.1:6391
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

1.3 測試
現在測試 Redis Cluster 是否能正常工作。
通過 Redis 測試客戶端命令redis-cli 連接到集羣任意一個節點:

#cd /usr/local/redis-4.0.11/src/        # 切換到啓動腳本目錄
#./redis-cli -c -h 127.0.0.1 -p 6390    # 以集羣方式連接到 Redis-6390 節點

在這裏插入圖片描述
這裏需要注意,與前面單機/主從/哨兵模式不同的是,客戶端命令 redis-cli 需要增加一個-c參數,表示是連接到集羣,這樣客戶端的讀寫行爲纔是在整個集羣中可見的。
若不加-c參數雖然也可連接,但是僅僅是連接到當前的節點,是無法進行數據讀寫的(除非所讀寫的數據的鍵值,經過 Hash 計算得到的 slot 槽號,剛好在這個節點裏面)。

現測試6390 節點執行get 命令,獲取鍵名爲name的數據:

127.0.0.1:6390> get name
-> Redirected to slot [5798] located at 127.0.0.1:6391
(nil)           # 由於是新部署的 Redis 集羣,該鍵值必定不存在

然後在 6390 節點寫入數據(執行 set 命令,爲鍵name設置數據):

127.0.0.1:6390> set name jingjing
-> Redirected to slot [5798] located at 127.0.0.1:6391
OK

再通過 Redis 測試客戶端命令redis-cli 連接到集羣另一個節點:

#cd /usr/local/redis-4.0.11/src/          # 切換到啓動腳本目錄
#./redis-cli -c -h 127.0.0.1 -p 6393      # 以集羣方式連接到 Redis-6393節點

在 6393節點執行get 命令,獲取鍵名爲name的數據(經過集羣節點轉發請求,可以取到數據):

127.0.0.1:6393> get name
-> Redirected to slot [5798] located at 127.0.0.1:6391
"jingjing"

現在嘗試一下不以集羣方式連接到節點 6393,看一下會發生什麼:

[root@localhost src]# ./redis-cli -h 127.0.0.1 -p 6393    # 以單點方式連接到 Redis-6393節點(無-c 參數)
127.0.0.1:6393> get name             # 獲取鍵值爲name的數據
(error) MOVED 5798 127.0.0.1:6391    # 報錯:該數據在 63921節點的5798槽位
127.0.0.1:6393> set name wen         # 設置鍵爲name的值
(error) MOVED 5798 127.0.0.1:6391    # 報錯:該鍵應該設置到6391節點的5798槽位

# 有些版本的報錯信息是這樣的,意思是相同的:
# -> Redirected to slot [5798] located at 127.0.0.1:6391

之所以會發生這種情況,其實最開始的 Redis Cluster 架構介紹的時候就已經解釋了原因了:
1.Redis Cluster 沒有使用一致性 Hash,而是採用 slot(槽)的概念,一共分成 16384 個槽。在構建集羣時,這些槽會不重複地平均被分配到集羣中的主節點。
2.在讀寫數據時,Redis Cluster 會先計算該數據的鍵值的 slot 號,然後再把讀寫請求分配到該slot 號所屬的 Redis 節點。
3.而當不以集羣方式連接到集羣節點時,在計算 slot 號後,讀寫請求的分配工作卻無法執行,就會出現上述報錯。

1.4集羣的關閉/重啓
Redis Cluster 的官方文檔並沒有提供整個集羣的關閉與重啓的方法。推測可能是由於集羣所有節點同時掛掉的可能性不高,畢竟即使偶爾集羣中某個節點掛掉了,待其重啓後又會自動重新加入集羣,並不會帶來太大影響。但是可能性不高並不代表不會發生,如何關閉/重啓整個集羣的方式還是需要知道的。

集羣的關閉,執行這個命令即可(如果是部署到多臺 IP 機器,需要登陸到每一臺機器執行):

#pkill redis       # 直接殺掉集羣的全部節點進程就可以了

集羣的重啓,只需要重新啓動原集羣中每一個節點就可以了,集羣會自動恢復到關閉之前的狀態(前提是不能刪除 node-.conf、.aof、*.rdb 文件,否則會造成集羣數據丟失)。

至此 Redis Cluster 集羣模式部署完成。

1.5集羣模式一鍵啓動/停止/重啓腳本
此腳本是以本實驗部署方式和位置爲基礎編寫的,僅適用於 Redis 節點均在同一臺機器的場景。若要在其他地方使用,需要根據實際情況修改。

腳本內容如下:

#!/bin/bash
# 根據主從/哨兵模式的部署目錄對應修改
export cluster_path=/usr/local/redis-cluster

# 啓動函數
start()
{
${cluster_path}/redis-6390/src/redis-server ${cluster_path}/redis-6390/redis.conf
${cluster_path}/redis-6391/src/redis-server ${cluster_path}/redis-6391/redis.conf
${cluster_path}/redis-6392/src/redis-server ${cluster_path}/redis-6392/redis.conf
${cluster_path}/redis-6393/src/redis-server ${cluster_path}/redis-6393/redis.conf
${cluster_path}/redis-6394/src/redis-server ${cluster_path}/redis-6394/redis.conf
${cluster_path}/redis-6395/src/redis-server ${cluster_path}/redis-6395/redis.conf
echo "all running"
}

# 停止函數
stop()
{
ps -ef | grep "redis" | grep -v "grep" |awk '{print $2}'| while read pid
do
C_PID=$(ps --no-heading $pid | wc -l)
if [[ $C_PID == "1" ]]; then
kill -9 $pid
echo "PID=$pid is dead"
else
echo "PID=$pid not exists"
fi
done
echo "all dead"
}

# 腳本入口參數 start|stop|restart
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
printf 'Usage: %s {start|stop|restart}\n'"$prog"
exit 1
;;
esac

這是 shell 腳本,保存爲 redis-cluster.sh(文件名任意)即可執行(若無法執行可能是換行符問題,可嘗試通過 dos2unix 命令修正換行符)。

使用方式如下:

sh redis-cluster.sh start      # 啓動
sh redis-cluster.sh stop       # 停止
sh redis-cluster.sh restart    # 重啓
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章