高可用架構篇
Redis 集羣的安裝(Redis3+CentOS)html
Redis 官方集羣指南:http://redis.io/topics/cluster-tutorial
Redis 官方集羣規範:http://redis.io/topics/cluster-spec
Redis 集羣指南(中文翻譯,緊供參考):http://redisdoc.com/topic/cluster-tutorial.html
Redis 集羣規範(中文翻譯,緊供參考): http://redisdoc.com/topic/cluster-spec.html (建議學員們在觀看視頻前,請先對以上參考文檔中的內容有個大概的瞭解
)java
簡單歸納:node
redis經過同一個節點的主從複製,數據分片,redis2.0的時候,主節點出現問題,可能就會出現問題,到了redis3.0,分爲多個主節點,每一個主節點分不一樣的數據,每一個節點主從複製,從而實現整個集羣的高可用和高可擴展。
每一個節點又能夠理解爲一個小的集羣,每一個節點集羣有主從複製,兩份相同的數據,全部的節點共同構成了整個rendis集羣的全部數據。
每一個節點的hash槽都設置了從節點,就實現了高可用linux
Redis 集羣數據共享
Redis 集羣使用數據分片(sharding)而非一致性哈希(consistency hashing)來實現: 一個 Redis 集羣包含 16384 個哈希槽(hash slot), 數據庫中的每一個鍵都屬於這 16384 個哈希槽的其中一個, 集羣使用公式 CRC16(key) % 16384 來計算鍵 key 屬於哪一個槽, 其中 CRC16(key) 語句用於計算鍵 key 的 CRC16 校驗和 。
集羣中的每一個節點負責處理一部分哈希槽。 舉個例子, 一個集羣能夠有三個哈希槽, 其中:git
節點 A 負責處理 0 號至 5500 號哈希槽。
節點 B 負責處理 5501 號至 11000 號哈希槽。
節點 C 負責處理 11001 號至 16384 號哈希槽。
這種將哈希槽分佈到不一樣節點的作法使得用戶能夠很容易地向集羣中添加或者刪除節點。 好比說:
若是用戶將新節點 D 添加到集羣中, 那麼集羣只須要將節點 A 、B 、 C 中的某些槽移動到節點 D 就能夠了。
與此相似, 若是用戶要從集羣中移除節點 A , 那麼集羣只須要將節點 A 中的全部哈希槽移動到節點 B 和節點 C , 而後再移除空白(不包含任何哈希槽)的節點 A 就能夠了。
由於將一個哈希槽從一個節點移動到另外一個節點不會形成節點阻塞, 因此不管是添加新節點仍是移除已存在節點, 又或者改變某個節點包含的哈希槽數量, 都不會形成集羣下線。github
Redis 集羣中的主從複製
爲了使得集羣在一部分節點下線或者沒法與集羣的大多數(majority)節點進行通信的狀況下, 仍然能夠正常運做, Redis 集羣對節點使用了主從複製功能: 集羣中的每一個節點都有 1 個至 N 個複製品(replica), 其中一個複製品爲主節點(master), 而其他的 N-1 個複製品爲從節點(slave)。web
在以前列舉的節點 A 、B 、C 的例子中, 若是節點 B 下線了, 那麼集羣將沒法正常運行, 由於集羣找不到節點來處理 5501 號至 11000 號的哈希槽。redis
另外一方面, 假如在建立集羣的時候(或者至少在節點 B 下線以前), 咱們爲主節點 B 添加了從節點 B1 , 那麼當主節點 B 下線的時候, 集羣就會將 B1 設置爲新的主節點, 並讓它代替下線的主節點 B , 繼續處理 5501 號至 11000 號的哈希槽, 這樣集羣就不會由於主節點 B 的下線而沒法正常運做了。算法
不過若是節點 B 和 B1 都下線的話, Redis 集羣仍是會中止運做。
Redis 集羣的一致性保證(guarantee)(簡單看,通常不會遇到)
Redis 集羣不保證數據的強一致性(strong consistency): 在特定條件下, Redis 集羣可能會丟失已經被執行過的寫命令。shell
使用異步複製(asynchronous replication)是 Redis 集羣可能會丟失寫命令的其中一個緣由。 考慮如下這個寫命令的例子:
客戶端向主節點 B 發送一條寫命令。
主節點 B 執行寫命令,並向客戶端返回命令回覆。
主節點 B 將剛剛執行的寫命令複製給它的從節點 B1 、 B2 和 B3 。
如你所見, 主節點對命令的複製工做發生在返回命令回覆以後, 由於若是每次處理命令請求都須要等待複製操做完成的話, 那麼主節點處理命令請求的速度將極大地下降 —— 咱們必須在性能和一致性之間作出權衡。
Note
若是真的有必要的話, Redis 集羣可能會在未來提供同步地(synchronou)執行寫命令的方法。
Redis 集羣另一種可能會丟失命令的狀況是, 集羣出現網絡分裂(network partition), 而且一個客戶端與至少包括一個主節點在內的少數(minority)實例被孤立。
舉個例子, 假設集羣包含 A 、 B 、 C 、 A1 、 B1 、 C1 六個節點, 其中 A 、B 、C 爲主節點, 而 A1 、B1 、C1 分別爲三個主節點的從節點, 另外還有一個客戶端 Z1 。
假設集羣中發生網絡分裂, 那麼集羣可能會分裂爲兩方, 大多數(majority)的一方包含節點 A 、C 、A1 、B1 和 C1 , 而少數(minority)的一方則包含節點 B 和客戶端 Z1 。
在網絡分裂期間, 主節點 B 仍然會接受 Z1 發送的寫命令:
若是網絡分裂出現的時間很短, 那麼集羣會繼續正常運行;
可是, 若是網絡分裂出現的時間足夠長, 使得大多數一方將從節點 B1 設置爲新的主節點, 並使用 B1 來代替原來的主節點 B , 那麼 Z1 發送給主節點 B 的寫命令將丟失。
注意, 在網絡分裂出現期間, 客戶端 Z1 能夠向主節點 B 發送寫命令的最大時間是有限制的, 這一時間限制稱爲節點超時時間(node timeout), 是 Redis 集羣的一個重要的配置選項:
對於大多數一方來講, 若是一個主節點未能在節點超時時間所設定的時限內從新聯繫上集羣, 那麼集羣會將這個主節點視爲下線, 並使用從節點來代替這個主節點繼續工做。
對於少數一方, 若是一個主節點未能在節點超時時間所設定的時限內從新聯繫上集羣, 那麼它將中止處理寫命令, 並向客戶端報告錯誤。
強烈建議六個節點,三個主三個從,若是隻有三個從,其中某個主節點掛了,這個節點的相關槽位的數據就不可用了。若是沒有從節點,redis集羣是沒有意義的,是不能高可用的。
要讓 Redis3.0 集羣正常工做至少須要 3 個 Master 節點,要想實現高可用,每一個 Master 節點要配備 至少 1 個 Slave 節點。根據以上特色和要求,進行以下的集羣實施規劃:
使用 6 臺服務器(物理機或虛擬機)部署 3 個 Master + 3 個 Slave;
圖中的藍綠一組能夠理解爲是一個redis集羣中的節點,數據如出一轍,只不過是一個主一個從,兩個組成一個小的集羣,保存整個redis集羣中的一小部分,來保證高可用,可是每兩個主節點的數據是不同的,經過算法算出來的,雖然數據不同,可是節點之間可以知道其餘節點的狀態,即可以知道key在對應的哪一個節點上
主機名 | IP | 服務端口 | 集羣端口 | 主/從 |
---|---|---|---|---|
默認 6379 | 服務端口數+10000 | |||
edu-redis-01 | 192.168.1.111 | 7111 | 17111 | Master |
edu-redis-02 | 192.168.1.112 | 7112 | 17112 | Master |
edu-redis-03 | 192.168.1.113 | 7113 | 17113 | Master |
edu-redis-04 | 192.168.1.114 | 7114 | 17114 | Slave |
edu-redis-05 | 192.168.1.115 | 7115 | 17115 | Slave |
edu-redis-06 | 192.168.1.116 | 7116 | 17116 | Slave |
注意:在配置文件中是沒有集羣端口這個配置的,由於默認是服務端口+10000
服務端口的做用是主節點宕機以後的選舉、互相知道其餘節點的狀態等做用。
按規劃:防火牆中打開相應的端口
192.168.1.111 -A INPUT -m state --state NEW -m tcp -p tcp --dport 7111 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17111 -j ACCEPT 192.168.1.112 -A INPUT -m state --state NEW -m tcp -p tcp --dport 7112 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17112 -j ACCEPT 192.168.1.113 -A INPUT -m state --state NEW -m tcp -p tcp --dport 7113 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17113 -j ACCEPT 192.168.1.114 -A INPUT -m state --state NEW -m tcp -p tcp --dport 7114 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17114 -j ACCEPT 192.168.1.115 -A INPUT -m state --state NEW -m tcp -p tcp --dport 7115 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17115 -j ACCEPT 192.168.1.116 -A INPUT -m state --state NEW -m tcp -p tcp --dport 7116 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17116 -j ACCEPT
安裝目錄:/usr/local/redis3 用戶:root
編譯和安裝所需的包:
# yum install gcc tcl
下載(或上傳)Redis3 最新穩定版(當前最新版 redis-3.0.3.tar.gz) # cd /usr/local/src
# wget http://download.redis.io/releases/redis-3.0.3.tar.gz
建立安裝目錄:
# mkdir /usr/local/redis3
解壓:
# tar -zxvf redis-3.0.3.tar.gz # cd redis-3.0.3
安裝(使用 PREFIX 指定安裝目錄,安裝到指定目錄):
# make PREFIX=/usr/local/redis3 install
安裝完成後,能夠看到/usr/local/redis3 目錄下有一個 bin 目錄,bin 目錄裏就是 redis 的命令腳本:
redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server
建立集羣配置目錄,並拷貝 redid.conf 配置文件到各節點配置目錄:
192.168.1.111 # mkdir -p /usr/local/redis3/cluster/7111 # cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7111/redis-7111.conf 192.168.1.112 # mkdir -p /usr/local/redis3/cluster/7112 # cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7112/redis-7112.conf 192.168.1.113 # mkdir -p /usr/local/redis3/cluster/7113 # cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7113/redis-7113.conf 192.168.1.114 # mkdir -p /usr/local/redis3/cluster/7114 # cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7114/redis-7114.conf 192.168.1.115 # mkdir -p /usr/local/redis3/cluster/7115 # cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7115/redis-7115.conf 192.168.1.116 # mkdir -p /usr/local/redis3/cluster/7116 # cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7116/redis-7116.conf
修改配置文件中的下面選項:
6 個節點的 redis.conf 配置文件內容,注意修改下紅色字體部分的內容便可,其餘都相同:
配置選項 | 選項值 | 說明 |
---|---|---|
daemonize | yes | 是否做爲守護進程運行 |
pidfile | /var/run/redis- 7111.pid | 如之後臺進程運行,則需指定一個 pid,默認爲/var/run/redis.pid |
** port | 7111 | 監聽端口,默認爲 6379,注意:集羣通信端口值默認爲此端口值+10000,如 17111 ** |
databases | 1 | 可用數據庫數,默認值爲 16,默認數據庫存儲在 DB 0號 ID 庫中,無特殊需求,建議僅設置一個數據庫databases 1 |
cluster-enabled | yes | 打開 redis 集羣 |
cluster-config-file | /usr/local/redis3/cluster/ 7111/nodes.conf | 集羣配置文件(啓動自動生成),不用人爲干涉 |
cluster-node-timeout | 15000 | 節點互連超時時間。毫秒 |
cluster-migration-barrier | 1 | 數據遷移的副本臨界數,這個參數表示的是,一個主節點在擁有多少個好的從節點的時候就要割讓一個從節點出來給另外一個沒有任何從節點的主節點。 |
cluster-require-full- | yes | 若是某一些 key space 沒有被集羣中任何節點覆蓋,集羣將中止接受寫入。 |
appendonly | yes | 啓用 aof 持久化方式由於 redis 自己同步數據文件是按上面 save 條件來同步的,因此有的數據會在一段時間內只存在於內存中。默認值爲 no |
dir | /usr/local/redis3/cluster/ 7111 | 節點數據持久化存放目錄(建議配置) |
紅色的部分是須要着重修改的地方,綠色是最少配置項,藍色是留心關注的知識點。
包含了最少選項的集羣配置文件示例以下:
port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
使用以下命令啓動這 6 個 Redis 節點實例:
192.168.1.111 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7111/redis-7111.conf 192.168.1.112 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7112/redis-7112.conf 192.168.1.113 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7113/redis-7113.conf 192.168.1.114 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7114/redis-7114.conf 192.168.1.115 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7115/redis-7115.conf 192.168.1.116 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7116/redis-7116.conf
啓動以後用 PS 命令查看實例啓動狀況:
[root@edu-redis-01 cluster]# ps -ef | grep redis root 5443 1 0 22:49 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7111 [cluster] [root@edu-redis-02 cluster]# ps -ef | grep redis root 5421 1 0 22:49 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7112 [cluster] [root@edu-redis-03 cluster]# ps -ef | grep redis root 5457 1 0 22:49 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7113 [cluster] [root@edu-redis-04 cluster]# ps -ef | grep redis root 5379 1 0 22:50 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7114 [cluster] [root@edu-redis-05 cluster]# ps -ef | grep redis root 5331 1 0 22:50 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7115 [cluster] [root@edu-redis-06 cluster]# ps -ef | grep redis root 5687 1 0 22:50 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7116 [cluster]
注意:啓動完畢後,6 個 Redis 實例還沒有構成集羣。
安裝 ruby 和 rubygems(注意:須要 ruby 的版本在 1.8.7 以上)
# yum install ruby rubygems
檢查 ruby 版本:
# ruby -v
ruby 1.8.7 (2013-06-27 patchlevel 374) [x86_64-linux]
gem 安裝 redis ruby 接口:
# gem install redis Successfully installed redis-3.2.1 1 gem installed Installing ri documentation for redis-3.2.1... Installing RDoc documentation for redis-3.2.1...
執行 Redis 集羣建立命令(只須要在其中一個節點上執行一次則可)
# cd /usr/local/src/redis-3.0.3/src/ # cp redis-trib.rb /usr/local/bin/redis-trib //replicas就是每一個主節點有幾個複製品,這裏的關係是456是master加點,123是複製品。 # redis-trib create --replicas 1 192.168.1.114:7114 192.168.1.115:7115 192.168.1.116:7116 192.168.1.111:7111 192.168.1.112:7112 192.168.1.113:7113 //先看看能不能連上 >>> Creating cluster Connecting to node 192.168.1.114:7114: OK Connecting to node 192.168.1.115:7115: OK Connecting to node 192.168.1.116:7116: OK Connecting to node 192.168.1.111:7111: OK Connecting to node 192.168.1.112:7112: OK Connecting to node 192.168.1.113:7113: OK >>> Performing hash slots allocation on 6 nodes... //而後選三個做爲master節點 Using 3 masters: 192.168.1.113:7113 192.168.1.112:7112 192.168.1.111:7111 //三個主節點的從節點分別是什麼 Adding replica 192.168.1.116:7116 to 192.168.1.113:7113 Adding replica 192.168.1.115:7115 to 192.168.1.112:7112 Adding replica 192.168.1.114:7114 to 192.168.1.111:7111 S: 007a3fe8d7451d3d0a78fffd2653c8641809499c 192.168.1.114:7114 replicates 94e140b9ca0735040ae3428983835f1d93327aeb S: ea69b6b6e2e7723eed50b1dabea9d244ccf3f098 192.168.1.115:7115 replicates c642b3071c4b2b073707ed3c3a2c16d53a549eff S: 5f09dc0671732cf06a09f28631c90e0c68408520 192.168.1.116:7116 replicates 896a3c99da4fcf680de1f42406fccb551d8c40c3 M: 94e140b9ca0735040ae3428983835f1d93327aeb 192.168.1.111:7111 slots:10923-16383 (5461 slots) master M: c642b3071c4b2b073707ed3c3a2c16d53a549eff 192.168.1.112:7112 slots:5461-10922 (5462 slots) master M: 896a3c99da4fcf680de1f42406fccb551d8c40c3 192.168.1.113:7113 slots:0-5460 (5461 slots) master Can I set the above configuration? (type 'yes' to accept): yes (輸入 yes 並按下回車確認以後,集羣就會將配置應用到各個節點,並鏈接起(join)各個節點,也就是 讓各個節點開始互相通信) >>> 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 192.168.1.114:7114) M: 007a3fe8d7451d3d0a78fffd2653c8641809499c 192.168.1.114:7114 slots: (0 slots) master replicates 94e140b9ca0735040ae3428983835f1d93327aeb M: ea69b6b6e2e7723eed50b1dabea9d244ccf3f098 192.168.1.115:7115 slots: (0 slots) master replicates c642b3071c4b2b073707ed3c3a2c16d53a549eff M: 5f09dc0671732cf06a09f28631c90e0c68408520 192.168.1.116:7116 slots: (0 slots) master replicates 896a3c99da4fcf680de1f42406fccb551d8c40c3 M: 94e140b9ca0735040ae3428983835f1d93327aeb 192.168.1.111:7111 slots:10923-16383 (5461 slots) master M: c642b3071c4b2b073707ed3c3a2c16d53a549eff 192.168.1.112:7112 slots:5461-10922 (5462 slots) master M: 896a3c99da4fcf680de1f42406fccb551d8c40c3 192.168.1.113:7113 slots:0-5460 (5461 slots) master
上面輸出信息的最後 slots中能夠看到將16383個hash槽平均分到了三個節點上。
在cluster文件夾下面的7116文件夾下有兩個配置文件nodes.conf和redis-7116.conf,前者是全部節點的信息,後者是某一個配置文件的詳細配置信息。
一切正常的狀況下輸出如下信息:
[OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
最後一行信息表示集羣中的 16384 個槽都有至少一個主節點在處理, 集羣運做正常。
集羣建立過程說明:
(1) 給定 redis-trib 程序的命令是 create , 這表示咱們但願建立一個新的集羣;
(2) 這裏的 --replicas 1 表示每一個主節點下有一個從節點;
(3) 以後跟着的其它參數則是實例的地址列表,程序使用這些地址所指示的實例來建立新集羣; 總的來講,以上命令的意思就是讓 redis-trib 程序建立一個包含三個主節點和三個從節點的集羣。 接着,redis-trib 會打印出一份預想中的配置給你看,若是你以爲沒問題的話(注意覈對主從關係是不是 你想要的
),就能夠輸入 yes , redis-trib 就會將這份配置應用到集羣當中。
使用 redis-cli 命令進入集羣環境
[root@edu-redis-04 bin]# ./redis-cli -c -p 7114 127.0.0.1:7114> set wusc WuShuicheng -> Redirected to slot [8559] located at 192.168.1.112:7112 OK [root@edu-redis-01 bin]# ./redis-cli -c -p 7111 127.0.0.1:7111> get wusc //經過key計算出hash值,將值的內容放到了對應的hash槽中,而後重定向到了對應的節點上去 //例如上面是在7114,這裏拿值的時候重定向到了7112 -> Redirected to slot [8559] located at 192.168.1.112:7112 "WuShuicheng" [root@edu-redis-02 bin]# ./redis-cli -c -p 7112 127.0.0.1:7112> get wusc "WuShuicheng" 127.0.0.1:7112> [root@edu-redis-01 bin]# ./redis-cli -p 7111 cluster nodes
查看節點的狀態
cd /usr/local/redis3/bin ./redis-cli -p 7113 cluster nodes
(非僞集羣適用,也就是每一個節點都單獨物理機部署的狀況下
):
按上面的操做步驟,Redis 的啓動腳本爲:/usr/local/src/redis-3.0.3/utils/redis_init_script
將啓動腳本複製到/etc/rc.d/init.d/目錄下,並命名爲 redis:
# cp /usr/local/src/redis-3.0.3/utils/redis_init_script /etc/rc.d/init.d/redis
編輯/etc/rc.d/init.d/redis,修改相應配置,使之能註冊成爲服務:
# vi /etc/rc.d/init.d/redis #!/bin/sh # # Simple Redis init.d script conceived to work on Linux systems # as it does use of the /proc filesystem. REDISPORT=6379 EXEC=/usr/local/bin/redis-server CLIEXEC=/usr/local/bin/redis-cli PIDFILE=/var/run/redis_${REDISPORT}.pid CONF="/etc/redis/${REDISPORT}.conf" case "$1" in start) if [ -f $PIDFILE ] then echo "$PIDFILE exists, process is already running or crashed" "WuShuicheng" else $EXEC $CONF fi ;; echo "Starting Redis server..." stop) if [ ! -f $PIDFILE ] then fi ;; *) else echo "$PIDFILE does not exist, process is not running" PID=$(cat $PIDFILE) echo "Stopping ..." $CLIEXEC -p $REDISPORT shutdown while [ -x /proc/${PID} ] do echo "Waiting for Redis to shutdown ..." sleep 1 done echo "Redis stopped" echo "Please use start or stop as first argument" ;; esac
查看以上 redis 服務腳本,關注標爲橙色的幾個屬性,作以下幾個修改的準備:
(1) 在腳本的第一行後面添加一行內容以下:
#chkconfig: 2345 80 90
(若是不添加上面的內容,在註冊服務時會提示:service redis does not support chkconfig)
(2) REDISPORT 端口修改各節點對應的端口;(注意,端口名將與下面的配置文件名有關
)
(3) EXEC=/usr/local/bin/redis-server 改成 EXEC=/usr/local/redis3/bin/redis-server
(4) CLIEXEC=/usr/local/bin/redis-cli 改成 CLIEXEC=/usr/local/redis3/bin/redis-cli
(5) 配置文件設置,對 CONF 屬性做以下調整:
CONF="/etc/redis/${REDISPORT}.conf"
改成 CONF="/usr/local/redis3/cluster/
{REDISPORT}.conf"
(6) 更改 redis 開啓的命令,之後臺運行的方式執行:
$EXEC $CONF & #「&」做用是將服務轉到後面運行
修改後的/etc/rc.d/init.d/redis 服務腳本內容爲(注意各節點的端口不一樣
):
#!/bin/sh #chkconfig: 2345 80 90 # # Simple Redis init.d script conceived to work on Linux systems # as it does use of the /proc filesystem. REDISPORT=7111 EXEC=/usr/local/redis3/bin/redis-server CLIEXEC=/usr/local/redis3/bin/redis-cli PIDFILE=/var/run/redis-${REDISPORT}.pid CONF="/usr/local/redis3/cluster/${REDISPORT}/redis-${REDISPORT}.conf " case "$1" in start) if [ -f $PIDFILE ] then else fi ;; stop) echo "$PIDFILE exists, process is already running or crashed" echo "Starting Redis server..." $EXEC $CONF & fi ;; *) if [ ! -f $PIDFILE ] then else echo "$PIDFILE does not exist, process is not running" PID=$(cat $PIDFILE) echo "Stopping ..." $CLIEXEC -p $REDISPORT shutdown while [ -x /proc/${PID} ] do echo "Waiting for Redis to shutdown ..." sleep 1 done echo "Redis stopped" echo "Please use start or stop as first argument" ;; esac
以上配置操做完成後,即可將 Redis 註冊成爲服務: # chkconfig --add redis
防火牆中打開對應的端口,各節點的端口不一樣(前面已操做則可跳過此步) # vi /etc/sysconfig/iptables
添加:
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7111 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17111 -j ACCEPT
重啓防火牆:
# service iptables restart
啓動 Redis 服務
# service redis start
將 Redis 添加到環境變量中: # vi /etc/profile 在最後添加如下內容:
## Redis env export PATH=$PATH:/usr/local/redis3/bin
使配置生效:
# source /etc/profile
如今就能夠直接使用 redis-cli 等 redis 命令了:
關閉 Redis 服務
# service redis stop
默認狀況下,Redis 未開啓安全認證,能夠經過/usr/local/redis3/cluster/7111/redis-7111.conf
的 requirepass 指定一個驗證密碼。
Redis 3.0 集羣搭建測試(一):http://blog.csdn.net/zhu_tianwei/article/details/44928779 Redis 3.0 集羣搭建測試(二):http://blog.csdn.net/zhu_tianwei/article/details/45009647 Redis 集羣要點:http://5i.io/redis-3-0-cluster-configuration/
高可用架構篇
Redis 集羣的高可用測試(含 Jedis 客戶端的使用)
注意:本節教程內容緊接上一節教程《Dubbo 視頻教程–高可用架構篇–第 05 節–Redis 集羣的安裝 (Redis3+CentOS)》的內容
一、 Jedis 客戶端建議升級到最新版(當前爲 2.7.3),這樣對 3.0.x 集羣有比較好的支持。
https://github.com/xetorthio/jedis
http://mvnrepository.com/artifact/redis.clients/jedis
二、 直接在 Java 代碼中連接 Redis 集羣:
// 數據庫連接池配置 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(100); config.setMaxIdle(50); config.setMinIdle(20); config.setMaxWaitMillis(6 * 1000); config.setTestOnBorrow(true); // Redis集羣的節點集合 Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>(); jedisClusterNodes.add(new HostAndPort("192.168.1.111", 7111)); jedisClusterNodes.add(new HostAndPort("192.168.1.112", 7112)); jedisClusterNodes.add(new HostAndPort("192.168.1.113", 7113)); jedisClusterNodes.add(new HostAndPort("192.168.1.114", 7114)); jedisClusterNodes.add(new HostAndPort("192.168.1.115", 7115)); jedisClusterNodes.add(new HostAndPort("192.168.1.116", 7116)); // 根據節點集創集羣連接對象 //JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes); // 節點,客戶端鏈接時的超時時間,最多重定向次數,連接池 JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, 2000, 100, config); int num = 1000; String key = "wusc"; String value = ""; for (int i=1; i <= num; i++){ // 存數據 jedisCluster.set(key+i, "WuShuicheng"+i); // 取數據 value = jedisCluster.get(key+i); log.info(key+i + "=" + value); // 刪除數據 //jedisCluster.del(key+i); //value = jedisCluster.get(key+i); //log.info(key+i + "=" + value); }
上述代碼中的redis重定向的意思,是由於在上面的安裝中也有涉及到,即當redis查找的key的hash值的槽位不在當前的節點的時候,就會重定向到擁有這個槽位的節點上去,返回一個重定向的信息。
下面是一個MOVED例子:
get x -move 3999 127.0.0.1:6381
三、Spring 配置 Jedis 連接 Redis3.0 集羣的配置:
<!-- Jedis連接池配置,注意:Jedis版本建議升級到最新(當前最新版爲2.7.2) --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="100" /> <property name="maxIdle" value="20" /> <property name="minIdle" value="10" /> <property name="blockWhenExhausted" value="true"></property> <property name="maxWaitMillis" value="3000" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <property name="testWhileIdle" value="true" /> <property name="minEvictableIdleTimeMillis" value="60000" /> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <property name="numTestsPerEvictionRun" value="-1" /> </bean> <!-- JedisCluster --> <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster"> <constructor-arg index="0"> <set> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.1.111" /> <constructor-arg index="1" value="7111" type="int" /> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.1.112" /> <constructor-arg index="1" value="7112" type="int" /> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.1.113" /> <constructor-arg index="1" value="7113" type="int" /> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.1.114" /> <constructor-arg index="1" value="7114" type="int" /> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.1.115" /> <constructor-arg index="1" value="7115" type="int" /> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.1.116" /> <constructor-arg index="1" value="7116" type="int" /> </bean> </set> </constructor-arg> <constructor-arg index="1" value="2000" type="int"></constructor-arg> <constructor-arg index="2" value="100" type="int"></constructor-arg> <constructor-arg index="3" ref="jedisPoolConfig"></constructor-arg> </bean>
對應的 Java 調用代碼樣例(詳細代碼請看視頻教程提供的 demo 源碼):
JedisCluster jedisCluster = (JedisCluster) context.getBean("jedisCluster"); int num = 1000; String key = "wusc"; String value = ""; for (int i=1; i <= num; i++){ // 存數據 jedisCluster.set(key+i, "WuShuicheng"+i); // 取數據 value = jedisCluster.get(key+i); log.info(key+i + "=" + value); // 刪除數據 //jedisCluster.del(key+i); }
四、測試操做,請看視頻教程。
1、Redis 集羣特色
一、集羣架構特色:
(1)全部的 redis 節點彼此互聯(PING-PONG 機制),內部使用二進制協議優化傳輸速度和帶寬;
(2)節點的 fail 是經過集羣中超過半數的節點檢測失效時才生效;
(3)客戶端與 redis 節點直連,不須要中間 proxy 層。客戶端不須要鏈接集羣全部節點,鏈接集羣中任何一 個可用節點便可;
(4)redis-cluster 把全部的物理節點映射到[0-16383]個 slot(哈希槽)上,cluster 負責維護
node<->slot<->value 。
二、集羣選舉容錯:
(1)節點失效選舉過程是集羣中全部 master 參與,若是半數以上 master 節點與當前被檢測 master 節點通 信檢測超時(cluster-node-timeout),就認爲當前 master 節點掛掉; (2):何時整個集羣不可用(cluster_state:fail)?
A:若是集羣任意 master 掛掉,且當前 master 沒有 slave。集羣進入 fail 狀態,也能夠理解成集羣的 slot 映射[0-16383]不完整時進入 fail 狀態。 ps : redis-3.0.0.rc1 加入 cluster-require-full- coverage 參數,默認關閉,打開集羣兼容部分失敗;
B:若是集羣超過半數以上 master 掛掉,不管是否有 slave 集羣進入 fail 狀態。 ps:當集羣不可用時, 全部對集羣的操做作都不可用,收到((error) CLUSTERDOWN The cluster is down)錯誤。
2、客戶端集羣命令
集羣
cluster info :打印集羣的信息
cluster nodes :列出集羣當前已知的全部節點(node),以及這些節點的相關信息。
節點
cluster meet :將 ip 和 port 所指定的節點添加到集羣當中,讓它成爲集羣的一份子。 cluster forget <node_id> :從集羣中移除 node_id 指定的節點。
cluster replicate <node_id> :將當前節點設置爲 node_id 指定的節點的從節點。
cluster saveconfig :將節點的配置文件保存到硬盤裏面。
槽(slot)
cluster addslots [slot …] :將一個或多個槽(slot)指派(assign)給當前節點。 cluster delslots [slot …] :移除一個或多個槽對當前節點的指派。
cluster flushslots :移除指派給當前節點的全部槽,讓當前節點變成一個沒有指派任何槽的節點。 cluster setslot node <node_id> :將槽 slot 指派給 node_id 指定的節點,若是槽已經指派給 另外一個節點,那麼先讓另外一個節點刪除該槽>,而後再進行指派。
cluster setslot migrating <node_id> :將本節點的槽 slot 遷移到 node_id 指定的節點中。 cluster setslot importing <node_id> :從 node_id 指定的節點中導入槽 slot 到本節點。 cluster setslot stable :取消對槽 slot 的導入(import)或者遷移(migrate)。
鍵
cluster keyslot :計算鍵 key 應該被放置在哪一個槽上。
cluster countkeysinslot :返回槽 slot 目前包含的鍵值對數量。
cluster getkeysinslot :返回 count 個 slot 槽中的鍵。
3、集羣高可用測試(主要看視頻的操做與解說)
一、重建集羣,步驟:
(1)關閉集羣的各節點; ps -fef |grep redis kill -9
(2)刪除各節點數據目錄下的 nodes.conf、appendonly.aof、dump.rdb;
在/usr/local/redis3/cluster/7111
//這個命令能夠直接將三個刪除到。 # rm -rf appendonly.aof | rm -rf dump.rdb | rm -rf nodes.conf
(3)從新啓用全部的節點
192.168.1.111
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7111/redis-7111.conf 192.168.1.112 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7112/redis-7112.conf 192.168.1.113 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7113/redis-7113.conf 192.168.1.114 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7114/redis-7114.conf 192.168.1.115 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7115/redis-7115.conf 192.168.1.116 # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7116/redis-7116.conf
(4)執行集羣建立命令(只須要在其中一個節點上執行一次則可)
# cd /usr/local/src/redis-3.0.3/src/ # cp redis-trib.rb /usr/local/bin/redis-trib # redis-trib create --replicas 1 192.168.1.114:7114 192.168.1.115:7115 192.168.1.116:7116 192.168.1.111:7111 192.168.1.112:7112 192.168.1.113:7113
二、查看當前集羣各節點的狀態
[root@edu-redis-01 7111]# /usr/local/redis3/bin/redis-cli -c -p 7111 127.0.0.1:7111> cluster nodes
三、使用 demo 應用向集羣寫入 1000 個鍵值數據
使用 /usr/local/redis3/bin/redis-cli -c -p 711X 命令登陸各節點,使用 keys * 查看各節點的全部 key
四、運行 demo 應用,獲取全部的鍵值數據 若是有空值則中止
五、模擬集羣節點宕機(實現故障轉移,可重點看視頻解說)
模擬用戶不斷的對redis進行操做
RedisClusterFailoverTest.java
/** 基於Dubbo的分佈式系統架構視頻教程,吳水成,wu-sc@foxmail.com,學習交流QQ羣:367211134 **/ /** * * @描述: Redis集羣測試 . * @做者: WuShuicheng . * @建立時間: 2015-3-23,上午1:30:40 . * @版本號: V1.0 . */ public class RedisClusterFailoverTest { private static final Log log = LogFactory.getLog(RedisClusterFailoverTest.class); public static void main(String[] args) { // 數據庫連接池配置 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(100); config.setMaxIdle(50); config.setMinIdle(20); config.setMaxWaitMillis(6 * 1000); config.setTestOnBorrow(true); // Redis集羣的節點集合 Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>(); jedisClusterNodes.add(new HostAndPort("192.168.1.111", 7111)); jedisClusterNodes.add(new HostAndPort("192.168.1.112", 7112)); jedisClusterNodes.add(new HostAndPort("192.168.1.113", 7113)); jedisClusterNodes.add(new HostAndPort("192.168.1.114", 7114)); jedisClusterNodes.add(new HostAndPort("192.168.1.115", 7115)); jedisClusterNodes.add(new HostAndPort("192.168.1.116", 7116)); try { // 根據節點集創集羣連接對象 //JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes); // 集羣各節點集合,超時時間,最多重定向次數,連接池 JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, 2000, 100, config); int num = 1000; String key = "wusc"; String value = ""; int count = 1; while(true){ for (int i=1; i <= num; i++){ try { // 存數據 //jedisCluster.set(key+i, "WuShuicheng"+i); // 取數據 value = jedisCluster.get(key+i); log.info(key+i + "=" + value); //若是取出的是空值,就會跳出這個循環、 if (value == null || "".equals(value)){ log.error("===>break" + key+i + " value is null"); break; } } catch (Exception e) { //出現問題就自動重連 log.error("====>", e); Thread.sleep(3000); continue; } // 刪除數據 //jedisCluster.del(key+i); //value = jedisCluster.get(key+i); //log.info(key+i + "=" + value); } log.info("===================================>count:" + count); if (value == null || "".equals(value)){ break; } //統計執行了幾回1000 count++; Thread.sleep(1000); } } catch (Exception e) { log.error("====>", e); } } }
(1)Jedis 客戶端循環操做集羣數據(模擬用戶持續使用系統)
(2)查看 Redis 集羣當前狀態(用於接下來作節點狀態變化對比)
(3)關閉其中一個 master 節點(7111) ps -ef |grep =====> kill
(4)觀察該 master 節點和對應的 slave 節點的狀態變化(請看視頻解說)
節點狀態 fail? 表示正在判斷是否失敗
節點狀態 fail 表示節點失敗,對應的 slave 節點提高爲 master
(5)再查看集羣狀態變化# /usr/local/src/redis-3.0.3/src/redis-trib.rb check 192.168.1.116:7116
由上可見,7114 節點替換 7111,由 slave 變成了 master,原來的7111已經從展現中去除掉了
此時再執行 demo 應用獲取全部的鍵值數據,依然正常,說明 slave 替換 master 成功,集羣正常。
六、恢復 fail 節點
(1)啓動 7111
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7111/redis-7111.conf
(2)查看集羣狀態
其中 7111 變成 7114 的 slave
七、觀察集羣節點切換過程當中,對客戶端的影響
JedisCluster 連接 Redis 集羣操做時遇到的幾個常見異常:
(1)重定向次數過多
redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?
解決方法: 初始化 JedisCluster 時,設定 JedisCluster 的 maxRedirections //集羣各節點集合,超時時間(默認 2 秒),最多重定向次數(默認 5),連接池
new JedisCluster(jedisClusterNodes, 2000, 100, config);
(2)集羣不能夠用
redis.clients.jedis.exceptions.JedisClusterException: CLUSTERDOWN The cluster is down 緣由:集羣節點狀態切換過程當中會出現臨時閃斷,客戶端重試操做則可。
即上面的代碼中,若是拋出異常直接cotinue了,下次取數據的時候繞過了剛剛原本要取的數據,能夠再下次取的時候,在取一次這個。
(3)連接超時
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out 解決方法: 初始化 JedisCluster 時,設定 JedisCluster 的 timeout(默認爲兩秒);也能夠修改源碼中的 默認時間。
八、總結:
優勢:
在 master 節點下線後,slave 節點會自動提高爲 master 節點,保存集羣持續提供服務;
fail 節點恢復後,會自動添加到集羣中,變成 slave 節點;
缺點:
因爲 redis 的複製使用異步機制,在自動故障轉移的過程當中,集羣可能會丟失寫命令。
然而 redis 幾 乎是同時執行(將命令恢復發送給客戶端,以及將命令複製到 slave 節點)這兩個操做,因此實際中,命令 丟失的窗口很是小。
注意:本節教程內容緊接上兩節教程《高可用架構篇–第 05 節–Redis 集羣的安裝(Redis3+CentOS)》和《高可用架構篇–第 06 節–Redis 集羣的高可用測試(含 Jedis 客戶端的使用)》的內容
主機名 | IP | 服務端口 默認 6379 | 集羣端口 服務端口值+10000 | 主/從 |
---|---|---|---|---|
edu-redis-07 | 192.168.1.117 | 7117 | 17117 | Master |
edu-redis-07 | 192.168.1.117 | 7118 | 17118 | Slave |
按規劃:在 192.168.1.117 的防火牆中打開相應的端口
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7117 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17117 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 7118 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 17118 -j ACCEPT
可請參考《高可用架構篇–第 05 節–Redis 集羣的安裝(Redis3+CentOS)》教程的文檔,命令以下: # yum install gcc tcl
# cd /usr/local/src # wget http://download.redis.io/releases/redis-3.0.3.tar.gz # mkdir /usr/local/redis3 # tar -zxvf redis-3.0.3.tar.gz # cd redis-3.0.3 # make PREFIX=/usr/local/redis3 install # yum install ruby rubygems # gem install redis
192.168.1.117
# mkdir -p /usr/local/redis3/cluster/7117 # mkdir -p /usr/local/redis3/cluster/7118 # cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7117/redis-7117.conf # cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7118/redis-7118.conf
提示:conf 配置文件具體內容請看教程提供的 redis-7117.conf 和 redis-7118.conf 配置文件,主要增 加了數據目錄 dir 屬性的配置。
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7117/redis-7117.conf # /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7118/redis-7118.conf # ps -ef | grep redis root 4865 1 0 01:01 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7117 [cluster] root 4869 1 0 01:01 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7118 [cluster]
[root@edu-redis-01 src]# /usr/local/src/redis-3.0.3/src/redis-trib.rb Usage: redis-trib <command> <options> <arguments ...> import set-timeout del-node create help add-node reshard fix check call host:port --from <arg> host:port milliseconds host:port node_id host1:port1 ... hostN:portN --replicas <arg> (show this help) new_host:new_port existing_host:existing_port --slave --master-id <arg> host:port --slots <arg> --to <arg> --yes --from <arg> host:port host:port host:port command arg arg .. arg For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
redis-trib.rb 命令參數說明:
call:執行 redis 命令
create:建立一個新的集羣(上一節教程有介紹)
add-node:將一個節點添加到集羣裏面,第一個是新節點 ip:port, 第二個是集羣中任意一個正常節點 ip:port,–master-id
reshard:從新分片 check:查看集羣信息
del-node:移除一個節點
add-node 將一個節點添加到集羣裏面, 第一個是新節點 ip:port,第二個是任意一個已存在節點 ip:port
# /usr/local/src/redis-3.0.3/src/redis-trib.rb add-node 192.168.1.117:7117 192.168.1.111:7111 >>> Adding node 192.168.1.117:7117 to cluster 192.168.1.111:7111 Connecting to node 192.168.1.111:7111: OK Connecting to node 192.168.1.116:7116: OK Connecting to node 192.168.1.113:7113: OK Connecting to node 192.168.1.112:7112: OK Connecting to node 192.168.1.115:7115: OK Connecting to node 192.168.1.114:7114: OK >>> Performing Cluster Check (using node 192.168.1.111:7111) M: cc50047487b52697d62b1a72b231b7c74e08e051 192.168.1.111:7111 slots:10923-16383 (5461 slots) master 1 additional replica(s) S: b21ae6d0a3e614e53bbc52639173ec3ad68044b5 192.168.1.116:7116 slots: (0 slots) slave replicates 041addd95fa0a15d98be363034e53dd06f69ef47 M: 041addd95fa0a15d98be363034e53dd06f69ef47 192.168.1.113:7113 slots:0-5460 (5461 slots) master 1 additional replica(s) M: 712e523b617eea5a2ed8df732a50ff298ae2ea48 192.168.1.112:7112 slots:5461-10922 (5462 slots) master 1 additional replica(s) S: 55c0db5af1b917f3ce0783131fb8bab28920e1f3 192.168.1.115:7115 slots: (0 slots) slave replicates 712e523b617eea5a2ed8df732a50ff298ae2ea48 S: 8a6ca1452d61f8b4726f0649e6ce49a6ec4afee2 192.168.1.114:7114 slots: (0 slots) slave replicates cc50047487b52697d62b1a72b231b7c74e08e051 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. Connecting to node 192.168.1.117:7117: OK >>> Send CLUSTER MEET to node 192.168.1.117:7117 to make it join the cluster. [OK] New node added correctly.
經過鏈接到redis數據庫(任意一個節點)執行cluster nodes命令,能查看集羣node轉狀態
以上操做結果表示節點添加成功,新增的節點不包含任何數據, 由於它沒有分配任何 slot。 新加入的節點是一個 master 節點,當集羣須要將某個從節點升級爲新的主節點時,這個新節點不會被選 中。
爲新節點分配哈希槽(slot):
你只須要指定集羣中其中一個節點的地址,redis-trib 就會自動找到集羣中的其餘節點。目前 redis-trib 只能在管理員的協助下完成從新分片的工做,命令以下:
# /usr/local/src/redis-3.0.3/src/redis-trib.rb reshard 192.168.1.111:7111 Connecting to node 192.168.1.111:7111: OK Connecting to node 192.168.1.117:7117: OK Connecting to node 192.168.1.116:7116: OK Connecting to node 192.168.1.113:7113: OK Connecting to node 192.168.1.112:7112: OK Connecting to node 192.168.1.115:7115: OK Connecting to node 192.168.1.114:7114: OK >>> Performing Cluster Check (using node 192.168.1.111:7111) M: cc50047487b52697d62b1a72b231b7c74e08e051 192.168.1.111:7111 slots:10923-16383 (5461 slots) master 1 additional replica(s) M: badbc0ffde2a3700df7e179d23fa2762108eabba 192.168.1.117:7117 slots: (0 slots) master 0 additional replica(s) S: b21ae6d0a3e614e53bbc52639173ec3ad68044b5 192.168.1.116:7116 slots: (0 slots) slave replicates 041addd95fa0a15d98be363034e53dd06f69ef47 M: 041addd95fa0a15d98be363034e53dd06f69ef47 192.168.1.113:7113 slots:0-5460 (5461 slots) master 1 additional replica(s) M: 712e523b617eea5a2ed8df732a50ff298ae2ea48 192.168.1.112:7112 slots:5461-10922 (5462 slots) master 1 additional replica(s) S: 55c0db5af1b917f3ce0783131fb8bab28920e1f3 192.168.1.115:7115 slots: (0 slots) slave replicates 712e523b617eea5a2ed8df732a50ff298ae2ea48 S: 8a6ca1452d61f8b4726f0649e6ce49a6ec4afee2 192.168.1.114:7114 slots: (0 slots) slave replicates cc50047487b52697d62b1a72b231b7c74e08e051 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. How many slots do you want to move (from 1 to 16384)? 500
上面的提示是確認你打算移動的哈希槽 slots 的數量(這裏槽數量設置爲 500)
除了移動的哈希槽數量以外, redis-trib 還須要知道從新分片的目標(target node), 也就是負責接收 這 500 個哈希槽的節點。指定目標須要使用節點的 ID , 而不是 IP 地址和端口。咱們打算向上面新增 的主節點來做爲目標, 它的 IP 地址和端口是 192.168.1.117:7117,而節點 ID 則是 badbc0ffde2a3700df7e179d23fa2762108eabba , 那麼咱們應該向 redis-trib 提供節點的 ID :
What is the receiving node ID? badbc0ffde2a3700df7e179d23fa2762108eabba
接下來 redis-trib 會向你詢問從新分片的源節點(source node), 也就是要從哪一個節點中取出 500 個 哈希槽,並將這些槽移動到目標節點上面。若是咱們不打算從特定的節點上取出指定數量的哈希槽, 那麼 能夠向 redis-trib 輸入 all , 這樣的話, 集羣中的全部主節點都會成爲源節點, redis-trib 將從各 個源節點中各取出一部分哈希槽, 湊夠 500 個, 而後移動到目標節點上面:
Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs. Source node #1:all
輸入 all 並按下回車以後, redis-trib 將打印出哈希槽的移動計劃:
Do you want to proceed with the proposed reshard plan (yes/no)? yes
若是你以爲沒問題的話, 就能夠輸入 yes 並再次按回車確認, redis-trib 就會正式開始執行從新分片 操做, 將指定的哈希槽從源節點一個個地移動到目標節點上面。
注意:能夠同步觀察從新分片是否會對客戶端的連續使用產生影響(結果:不影響,經過上節中的demo:RedisClusterFailoverTest得出此結論)。
移動前,7117 上沒有 slot:
移動後,7117 上有 3 段 slot:
在從新分片操做執行完畢以後, 可使用如下命令來檢查集羣是否正常:
# /usr/local/src/redis-3.0.3/src/redis-trib.rb check 192.168.1.111:7111
上面輸出的檢查結果顯示,從新分片成功,集羣狀態正常。也能夠用如下命令再次查看集羣的節點情況:
# /usr/local/redis3/bin/redis-cli -c -p 7111 cluster nodes
以上集羣狀態輸出信息解析:
(1)節點 ID
(2)IP:PORT
(3)節點狀態標識: master、slave、myself、fail?、fail (4)若是是從節點,表示主節點的 ID;若是是主節點,則爲 ‘-’ (5)集羣最近一次向各個節點發送 PING 命令後,過去多長時間尚未接到回覆 (6)節點最近一次返回 PONG 的時間戳
(7)節點的配置紀元
(8)本節點的網絡鏈接狀況: connected、disconnected (9)若是是主節點,表示節點包含的槽
(1)添加節點
#/usr/local/src/redis-3.0.3/src/redis-trib.rb add-node 192.168.1.117:7118 192.168.1.111:7111 >>> Adding node 192.168.1.117:7118 to cluster 192.168.1.111:7111 Connecting to node 192.168.1.111:7111: OK Connecting to node 192.168.1.117:7117: OK Connecting to node 192.168.1.116:7116: OK Connecting to node 192.168.1.113:7113: OK Connecting to node 192.168.1.112:7112: OK Connecting to node 192.168.1.115:7115: OK Connecting to node 192.168.1.114:7114: OK >>> Performing Cluster Check (using node 192.168.1.111:7111) M: cc50047487b52697d62b1a72b231b7c74e08e051 192.168.1.111:7111 slots:11089-16383 (5295 slots) master 1 additional replica(s) M: badbc0ffde2a3700df7e179d23fa2762108eabba 192.168.1.117:7117 slots:0-165,5461-5627,10923-11088 (499 slots) master 0 additional replica(s) S: b21ae6d0a3e614e53bbc52639173ec3ad68044b5 192.168.1.116:7116 slots: (0 slots) slave replicates 041addd95fa0a15d98be363034e53dd06f69ef47 M: 041addd95fa0a15d98be363034e53dd06f69ef47 192.168.1.113:7113 slots:166-5460 (5295 slots) master 1 additional replica(s) M: 712e523b617eea5a2ed8df732a50ff298ae2ea48 192.168.1.112:7112 slots:5628-10922 (5295 slots) master 1 additional replica(s) S: 55c0db5af1b917f3ce0783131fb8bab28920e1f3 192.168.1.115:7115 slots: (0 slots) slave replicates 712e523b617eea5a2ed8df732a50ff298ae2ea48 S: 8a6ca1452d61f8b4726f0649e6ce49a6ec4afee2 192.168.1.114:7114 slots: (0 slots) slave replicates cc50047487b52697d62b1a72b231b7c74e08e051 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. Connecting to node 192.168.1.117:7118: OK >>> Send CLUSTER MEET to node 192.168.1.117:7118 to make it join the cluster. [OK] New node added correctly.
新增的 7118 爲一個 master
(2)redis-cli 鏈接上新節點 shell,輸入命令:cluster replicate 對應 master 的 node-id
# /usr/local/redis3/bin/redis-cli -c -p 7118 127.0.0.1:7118>cluster replicate ab31611b3424990e2b9bbe73135cb4cb0ace394f OK
ab31611b3424990e2b9bbe73135cb4cb0ace394f就是上面的7117
在線添加 slave 時,須要 dump 整個 master 進程,並傳遞到 slave,再由 slave 加載 rdb 文件到內存,rdb 傳輸過程當中 Master 可能沒法提供服務,整個過程消耗大量 IO,所以要當心操做。儘可能選擇用戶低峯值的時候。
查看執行結果:
127.0.0.1:7116> cluster nodes
這時 7117 已變成 7118 的 slave
對於負載/數據不均勻的狀況,能夠在線reshard slot來解決,方法與添加新master的reshard同樣,只 是須要 reshard 的 master 節點是老節點。
# cd /usr/local/src/redis-3.0.3/src/ //這裏注意是腳本del-node,後面的兩個參數依次是slave節點的ip端口和,id # ./redis-trib.rb del-node 192.168.1.117:7118 5256e05a17c106c93285a03aff1b1b9e7ca7bf0c
這時候,用下如下再查看集羣狀態,會發現該 slave 節點已成功移除
# /usr/local/redis3/bin/redis-cli -c -p 7116 cluster nodes
移除一個節點,對應的節點進程也會被關閉。
刪除 master 節點以前首先要使用 reshard 移除該 master 的所有 slot,而後再刪除當前節點(目前只能把被 刪除 master 的 slot 遷移到一個節點上),操做和分配 slot 相似,指定具體的 Source node 便可。
# /usr/local/src/redis-3.0.3/src/redis-trib.rb reshard 192.168.1.117:7117 Connecting to node 192.168.1.117:7117: OK Connecting to node 192.168.1.112:7112: OK Connecting to node 192.168.1.115:7115: OK Connecting to node 192.168.1.114:7114: OK Connecting to node 192.168.1.111:7111: OK Connecting to node 192.168.1.116:7116: OK Connecting to node 192.168.1.113:7113: OK >>> Performing Cluster Check (using node 192.168.1.117:7117) M: ab31611b3424990e2b9bbe73135cb4cb0ace394f 192.168.1.117:7117 slots:0-165,5461-5627,10923-11088 (499 slots) master 0 additional replica(s) M: d2c6c159b07e8197e2c8d2eae8c847050159f602 192.168.1.112:7112 slots:5628-10922 (5295 slots) master 1 additional replica(s) S: f34b28f1483f0c0d9543e93938fc12b8818050cb 192.168.1.115:7115 slots: (0 slots) slave replicates d2c6c159b07e8197e2c8d2eae8c847050159f602 M: 48db78bcc55c4c3a3788940a6458b921ccf95d44 192.168.1.114:7114 slots:11089-16383 (5295 slots) master 1 additional replica(s) S: 8dd55e9b4da9f62b9b15232e86553f1337864179 192.168.1.111:7111 slots: (0 slots) slave replicates 48db78bcc55c4c3a3788940a6458b921ccf95d44 S: 1fd90d54090925afb4087d4ef94a1710a25160d6 192.168.1.116:7116 slots: (0 slots) slave replicates 4e46bd06654e8660e617f7249fa22f6fa1fdff0d M: 4e46bd06654e8660e617f7249fa22f6fa1fdff0d 192.168.1.113:7113 slots:166-5460 (5295 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. //輸入被刪除 master 的全部 slot 數量 How many slots do you want to move (from 1 to 16384)? 499 //接收 slot 的 master 節點 ID What is the receiving node ID? 48db78bcc55c4c3a3788940a6458b921ccf95d44 Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs. //準備被刪除 master 節點的 node-id Source node #1: ab31611b3424990e2b9bbe73135cb4cb0ace394f Source node #2:done Ready to move 499 slots. Source nodes: M: ab31611b3424990e2b9bbe73135cb4cb0ace394f 192.168.1.117:7117 slots:0-165,5461-5627,10923-11088 (499 slots) master 0 additional replica(s) Destination node: M: 48db78bcc55c4c3a3788940a6458b921ccf95d44 192.168.1.114:7114 slots:11089-16383 (5295 slots) master 1 additional replica(s) Resharding plan: Moving slot 0 from ab31611b3424990e2b9bbe73135cb4cb0ace394f Moving slot 1 from ab31611b3424990e2b9bbe73135cb4cb0ace394f ...... //輸入 yes 執行 reshard Do you want to proceed with the proposed reshard plan (yes/no)? yes
移除該 Master 節點的全部 slot 後,從新查看集羣狀態,會發現該節點再也不佔用 slot:
# /usr/local/redis3/bin/redis-cli -c -p 7116 cluster nodes
能夠發現是master節點,可是後面沒有槽位
確認已清空該 Master 節點的全部 slot 後就能夠刪除該節點了(命令與刪除 slave 節點同樣):
# cd /usr/local/src/redis-3.0.3/src/ # ./redis-trib.rb del-node 192.168.1.117:7117 ab31611b3424990e2b9bbe73135cb4cb0ace394f
此時執行 ps -ef | grep redis 命令會發現,被移除的節點對應用的 Redis 實例也已經被關閉了