redis高階應用
Redis叢集簡介
Redis 叢集是3.0之後才引入的,在3.0之前,使用哨兵(sentinel)機制(本文將不做介紹,大家可另行查閱)來監控各個節點之間的狀態。Redis 叢集可謂是讓很多人久等了。
Redis 叢集是一組能進行資料共享的Redis 例項(服務或者節點)的設施,叢集可以使用的功能是普通單機 Redis 所能使用的功能的一個子集;Redis 叢集通常具有高可用、可擴充套件性、分散式、容錯等特性。瞭解redis的集群后,這些晦澀的概念可結合redis的主從、叢集分割槽和叢集運維等角度理解體會。
Redis叢集搭建
建立叢集資料夾
在/usr/local/下新建redis-cluster目錄並在redis-cluster下新建7031~7036共6個資料夾,這6個資料夾代表建立redis叢集的6個節點。如下
[[email protected] local]# mkdir -p /usr/local/redis-cluster
[[email protected] redis-cluster]# mkdir 7031 7032 7033 7034 7035 7036
拷貝修改配置檔案
將已有的/usr/local/redis/etc/下的redis.conf拷貝到新建立的7031目錄中
[[email protected] etc]# cp redis.conf /usr/local/redis-cluster/7031
[[email protected]
修改項如下:
(1)繫結埠,port 7031
(2)繫結IP,bind 192.168.2.128
(3)指定資料存放路徑,dir /usr/local/redis-cluster/7031
(4)啟動叢集模式,cluster-enabled yes
(5)指定叢集節點配置檔案,cluster-config-file nodes-7031.conf
(6)後臺啟動,daemonize yes
(7)指定叢集節點超時時間,cluster-node-timeout 5000
(8)指定持久化方式,appendonly yes
上面紅色專案最好全部設定,不然會出意想不到的錯誤
將7031的redis.conf改完後再拷貝到剩下的5個目錄中,然後只要全域性替換redis.conf中的7031為相應的節點即可。
安裝ruby
由於Redis 叢集客戶端實現很少,redis叢集的啟動需要用到ruby實現的redis-trib.rb,所以我們需要先安裝ruby。
[[email protected] redis-cluster]# yum install ruby
[[email protected] redis-cluster]# yum install rubygems
[[email protected] redis-cluster]# gem install redis
啟動redis例項
[[email protected] redis-cluster]#
/usr/local/redis/bin/redis-server /usr/local/redis-cluster/7031/redis.conf
分別啟動6個redis例項。也可以用指令碼迴圈啟動,這樣更方便省時
[[email protected] redis-cluster]#
for((i=1;i<=6;i++)); do /usr/local/redis/bin/redis-server /usr/local/redis-cluster/703$i/redis.conf; done
檢視redis例項是否啟動成功
[[email protected] redis-cluster]# netstat -tunpl | grep redis-server
#或者
[[email protected] redis-cluster]# ps -ef | grep redis-server
建立並啟動叢集
進入redis安裝目錄的bin目錄下
[[email protected] ~]# cd /usr/local/redis/bin/
[[email protected] bin]#./redis-trib.rb create --replicas 1 192.168.2.128:7031 192.168.2.128:7032 192.168.2.128:7033 192.168.2.128:7034 192.168.2.128:7035 192.168.2.128:7036
命令的意義如下:
給定 redis-trib.rb 程式的命令是 create,表示建立一個新的叢集。選項 --replicas 1 表示為叢集中的每個主節點建立一個從節點。之後跟著的其他引數則是例項的地址列表, 指定使用這些地址所指示的例項來建立新叢集。
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.2.128:7031
192.168.2.128:7032
192.168.2.128:7033
Adding replica 192.168.2.128:7034 to 192.168.2.128:7031
Adding replica 192.168.2.128:7035 to 192.168.2.128:7032
Adding replica 192.168.2.128:7036 to 192.168.2.128:7033
......
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
“All 16384 slots covered.”表示叢集中的 16384 個槽都有至少一個主節點在處理, 叢集運作正常。從打印出來的資訊也可以看出,7031,7032,7033是主節點,其它三個是從節點。
客戶端連線叢集
叢集啟動成功後,我們就可以用任意一個客戶端連線叢集了,如下
[[email protected] bin]# /usr/local/redis/bin/redis-cli -c -h 192.168.2.128 -p 7031
192.168.2.128:7031> info
# Server
redis_version:3.2.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:f8fcffd133fe3364
redis_mode:cluster
os:Linux 2.6.32-504.el6.x86_64 x86_64
arch_bits:64
可以使用 cluster info命令檢視叢集資訊,cluster nodes命令檢視叢集節點資訊。
叢集關閉
關閉叢集需要逐個關閉
[[email protected] redis-cluster]#
for((i=1;i<=6;i++)); do /usr/local/redis/bin/redis-cli -c -h 192.168.2.128 -p 703$i shutdown; done
如果重新啟動叢集報以下錯誤
[ERR] Node 192.168.2.128:7031 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
需要清除殺掉redis例項,然後刪除每個節點下的臨時資料檔案appendonly.aof,dump.rdb,nodes-703x.conf,然後再重新啟動redis例項即可啟動叢集。
[[email protected] redis-cluster]#for((i=1;i<=6;i++)); do cd 703$i; rm -rf appendonly.aof; rm -rf dump.rdb; rm -rf nodes-703$i.conf; cd ..; done
叢集測試
下面我們先來體驗一下叢集的set,get簡單操作,後面我們會進一步學習叢集的更多操作。
192.168.2.128:7031> set name "zhangsan"
-> Redirected to slot [5798] located at 192.168.2.128:7032
OK
192.168.2.128:7032> set age 20
-> Redirected to slot [741] located at 192.168.2.128:7031
OK
192.168.2.128:7031> set sex "man"
OK
192.168.2.128:7031>
如果在連線客戶端的時候不加-c選項set key時則會報MOVED 的錯誤:
[[email protected] bin]# /usr/local/redis/bin/redis-cli -h 192.168.2.128 -p 7031
192.168.2.128:7031> set name "zhangsan"
(error) MOVED 5798 192.168.2.128:7032
192.168.2.128:7031> set age 20
OK
192.168.2.128:7031> set sex "man"
OK
192.168.2.128:7031>
來看看取值又是怎麼樣
192.168.2.128:7031> get name
-> Redirected to slot [5798] located at 192.168.2.128:7032
"zhangsan"
192.168.2.128:7032> get age
-> Redirected to slot [741] located at 192.168.2.128:7031
"20"
192.168.2.128:7031> get sex
"man"
192.168.2.128:7031>
可以看到,客戶端連線加-c選項的時候,儲存和提取key的時候不斷在7031和7032之間跳轉,這個稱為客戶端重定向。之所以發生客戶端重定向,是因為Redis Cluster中的每個Master節點都會負責一部分的槽(slot),存取的時候都會進行鍵值空間計算定位key對映在哪個槽(slot)上,如果對映的槽(slot)正好是當前Master節點負責則直接存取,否則就跳轉到其他Master節點負的槽(slot)中存取,這個過程對客戶端是透明的。繼續看下文的叢集分割槽原理。
Redis叢集分割槽原理
槽(slot)的基本概念
從上面叢集的簡單操作中,我們已經知道redis存取key的時候,都要定位相應的槽(slot)。
Redis 叢集鍵分佈演算法使用資料分片(sharding)而非一致性雜湊(consistency hashing)來實現: 一個 Redis 叢集包含 16384 個雜湊槽(hash slot), 它們的編號為0、1、2、3……16382、16383,這個槽是一個邏輯意義上的槽,實際上並不存在。redis中的每個key都屬於這 16384 個雜湊槽的其中一個,存取key時都要進行key->slot的對映計算。
下面我們來看看啟動叢集時候列印的資訊:
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.2.128:7031
192.168.2.128:7032
192.168.2.128:7033
Adding replica 192.168.2.128:7034 to 192.168.2.128:7031
Adding replica 192.168.2.128:7035 to 192.168.2.128:7032
Adding replica 192.168.2.128:7036 to 192.168.2.128:7033
M: bee706db5ae182c5be9b9bdf94c2d6f3f8c8ec5c 192.168.2.128:7031
slots:0-5460 (5461 slots) master
M: 72826f06dbf3be163f2f456ca24caed76a15bdf4 192.168.2.128:7032
slots:5461-10922 (5462 slots) master
M: ab6e9d1dfc471225eef01e57be563157f81d26b3 192.168.2.128:7033
slots:10923-16383 (5461 slots) master
......
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
從上面資訊可以看出,建立叢集的時候,雜湊槽被分配到了三個主節點上,從節點是沒有雜湊槽的。7031負責編號為0-5460 共5461個 slots,7032負責編號為 5461-10922共5462 個 slots,7033負責編號為10923-16383 共5461個 slots。
鍵-槽對映演算法
和memcached一樣,redis也採用一定的演算法進行鍵-槽(key->slot)之間的對映。memcached採用一致性雜湊(consistency hashing)演算法進行鍵-節點(key-node)之間的對映,而redis叢集使用叢集公式來計算鍵 key 屬於哪個槽:
HASH_SLOT(key)= CRC16(key) % 16384
其中 CRC16(key) 語句用於計算鍵 key 的 CRC16 校驗和 。key經過公式計算後得到所對應的雜湊槽,而雜湊槽被某個主節點管理,從而確定key在哪個主節點上存取,這也是redis將資料均勻分佈到各個節點上的基礎。
鍵-槽-節點(key->slot->node)對映示意圖
叢集分割槽好處
無論是memcached的一致性雜湊演算法,還是redis的叢集分割槽,最主要的目的都是在移除、新增一個節點時對已經存在的快取資料的定位影響儘可能的降到最小。redis將雜湊槽分佈到不同節點的做法使得使用者可以很容易地向叢集中新增或者刪除節點, 比如說:
l 如果使用者將新節點 D 新增到叢集中, 那麼叢集只需要將節點 A 、B 、 C 中的某些槽移動到節點 D 就可以了。
l 與此類似, 如果使用者要從叢集中移除節點 A , 那麼叢集只需要將節點 A 中的所有雜湊槽移動到節點 B 和節點 C , 然後再移除空白(不包含任何雜湊槽)的節點 A 就可以了。
因為將一個雜湊槽從一個節點移動到另一個節點不會造成節點阻塞, 所以無論是新增新節點還是移除已存在節點, 又或者改變某個節點包含的雜湊槽數量, 都不會造成叢集下線,從而保證叢集的可用性。下面我們就來學習下叢集中節點的增加和刪除。
叢集操作
叢集操作包括檢視叢集資訊,檢視叢集節點資訊,向叢集中增加節點、刪除節點,重新分配槽等操作。
檢視叢集資訊
cluster info 檢視叢集狀態,槽分配,叢集大小等,cluster nodes也可檢視主從節點。
192.168.2.128:7031> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_sent:119
cluster_stats_messages_received:119
192.168.2.128:7031>
新增節點
(1)新增節點配置檔案
執行下面的指令碼建立指令碼配置檔案
[[email protected] redis-cluster]# mkdir /usr/local/redis-cluster/7037 && cp /usr/local/redis-cluster/7031/redis.conf /usr/local/redis-cluster/7037/redis.conf && sed -i "s/7031/7037/g" /usr/local/redis-cluster/7037/redis.conf
(2)啟動新增節點
[[email protected] bin]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7037/redis.conf
(3)新增節點到叢集
現在已經添加了新增一個節點所需的配置檔案,但是這個這點還沒有新增到叢集中,現在讓它成為叢集中的一個主節點
[[email protected] redis-cluster]# cd /usr/local/redis/bin/
[[email protected] bin]# ./redis-trib.rb add-node 192.168.2.128:7037 192.168.2.128:7036
>>> Adding node 192.168.2.128:7037 to cluster 192.168.2.128:7036
>>> Performing Cluster Check (using node 192.168.2.128:7036)
S: 2c8d72f1914f9d6052065f7e9910cc675c3c717b 192.168.2.128:7036
slots: (0 slots) slave
replicates 6dbb4aa323864265c9507cf336ef7d3b95ea8d1b
M: 6dbb4aa323864265c9507cf336ef7d3b95ea8d1b 192.168.2.128:7033
slots:10923-16383 (5461 slots) master
1 additional replica(s)
S: 791a7924709bfd7ef5c36d9b9c838925e41e3c2e 192.168.2.128:7034
slots: (0 slots) slave
replicates d9e3c78a7c49689c29ab67a8a17be9d95cb08452
M: d9e3c78a7c49689c29ab67a8a17be9d95cb08452 192.168.2.128:7031
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: 69b63d8db629fa8a689dd1ed25ed941c076d4111 192.168.2.128:7032
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: e669a91866225279aafcac29bf07b826eb5be91c 192.168.2.128:7035
slots: (0 slots) slave
replicates 69b63d8db629fa8a689dd1ed25ed941c076d4111
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 192.168.2.128:7037 to make it join the cluster.
[OK] New node added correctly.
[[email protected] bin]#
./redis-trib.rb add-node 命令中,7037 是新增的主節點,7036 是叢集中已有的從節點。再來看看叢集資訊
192.168.2.128:7031> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:7
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_sent:11256
cluster_stats_messages_received:11256
(4)分配槽
從新增主節點輸出資訊和檢視叢集資訊中可以看出,我們已經成功的向叢集中添加了一個主節點,但是這個主節還沒有成為真正的主節點,因為還沒有分配槽(slot),也沒有從節點,現在要給它分配槽(slot)
[[email protected] bin]# ./redis-trib.rb reshard 192.168.2.128:7031
>>> Performing Cluster Check (using node 192.168.2.128:7031)
M: 1a544a9884e0b3b9a73db80633621bd90ceff64a 192.168.2.128:7031
......
[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)? 1024
What is the receiving node ID?
系統提示要移動多少個配槽(slot),並且配槽(slot)要移動到哪個節點,任意輸入一個數,如1024,再輸入新增節點的ID cf48228259def4e51e7e74448e05b7a6c8f5713f.
What is the receiving node ID? cf48228259def4e51e7e74448e05b7a6c8f5713f
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:
然後提示要從哪幾個節點中移除1024個槽(slot),這裡輸入‘all’表示從所有的主節點中隨機轉移,湊夠1024個雜湊槽,然後就開始從新分配槽(slot)了。從新分配完後再次檢視叢集節點資訊
可見,0-340 5461-5802 10923-11263的槽(slot)被分配給了新增加的節點。三個加起來剛好1024個槽(slot)。
(5)指定從節點
現在從節點7036的主節點是7033,現在我們要把他變為新增加節點(7037)的從節點,需要登入7036的客戶端
[[email protected] bin]# /usr/local/redis/bin/redis-cli -c -h 192.168.2.128 -p 7036
192.168.2.128:7036> cluster replicate cf48228259def4e51e7e74448e05b7a6c8f5713f
OK
再來檢視叢集節點資訊
可見,7036成為了新增節點7037的從節點。
刪除節點
指定刪除節點的ID即可,如下
[[email protected] bin]#
./redis-trib.rb del-node 192.168.2.128:7037 'a56461a171334560f16652408c2a45e629d268f6'
>>> Removing node a56461a171334560f16652408c2a45e629d268f6 from cluster 192.168.2.128:7037
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
[[email protected] bin]#
叢集操作小結
從上面過程可以看出,新增節點、分配槽、刪除節點的過程,不用停止叢集,不阻塞叢集的其他操作。命令小結
#向叢集中新增節點,7037是新增節點,7036是叢集中已有的節點
./redis-trib.rb add-node 192.168.2.128:7037 192.168.2.128:7036
#重新分配槽
./redis-trib.rb reshard 192.168.2.128:7031
#指定當前節點的主節點
cluster replicate cf48228259def4e51e7e74448e05b7a6c8f5713f
#刪除節點
./redis-trib.rb del-node 192.168.2.128:7037 'a56461a171334560f16652408c2a45e629d268f6'
到此,redis的叢集搭建、分割槽原理、叢集增加節點以及刪除節點的主要內容已經簡要介紹完畢。