歡迎光臨
每天分享高質量文章

Redis 集群的原理和搭建

(點擊上方公號,快速關註我們)


來源:zgj12138

blog.csdn.net/zgj12138/article/details/74857510


前言

Redis 是我們目前大規模使用的快取中間件,由於它強大高效而又便捷的功能,得到了廣泛的使用。單節點的Redis已經就達到了很高的性能,為了提高可用性我們可以使用Redis集群。本文參考了Rdis的官方文件和使用Redis官方提供的Redis Cluster工具搭建Rdis集群。


註意 :Redis的版本要在3.0以上,截止今天,Redis的版本是3.2.9,本教程也使用3.2.9作為教程 


Redis集群的概念

介紹

Redis 集群是一個可以在多個 Redis 節點之間進行資料共享的設施(installation)。


Redis 集群不支持那些需要同時處理多個鍵的 Redis 命令, 因為執行這些命令需要在多個 Redis 節點之間移動資料, 並且在高負載的情況下, 這些命令將降低 Redis 集群的性能, 並導致不可預測的錯誤。


Redis 集群通過分割槽(partition)來提供一定程度的可用性(availability): 即使集群中有一部分節點失效或者無法進行通訊, 集群也可以繼續處理命令請求。


Redis 集群提供了以下兩個好處:

  1. 將資料自動切分(split)到多個節點的能力。

  2. 當集群中的一部分節點失效或者無法進行通訊時, 仍然可以繼續處理命令請求的能力。


資料分片

Redis 集群使用資料分片(sharding)而非一致性哈希(consistency hashing)來實現: 一個 Redis 集群包含 16384 個哈希槽(hash slot), 資料庫中的每個鍵都屬於這 16384 個哈希槽的其中一個, 集群使用公式 CRC16(key) % 16384 來計算鍵 key 屬於哪個槽, 其中 CRC16(key) 陳述句用於計算鍵 key 的 CRC16 校驗和 。


集群中的每個節點負責處理一部分哈希槽。 舉個例子, 一個集群可以有三個哈希槽, 其中:

  • 節點 A 負責處理 0 號至 5500 號哈希槽。

  • 節點 B 負責處理 5501 號至 11000 號哈希槽。

  • 節點 C 負責處理 11001 號至 16384 號哈希槽。

這種將哈希槽分佈到不同節點的做法使得用戶可以很容易地向集群中添加或者刪除節點。 比如說: 

我現在想設置一個key,叫my_name:

set my_name zhangguoji


按照Redis Cluster的哈希槽演算法,CRC16(‘my_name’)%16384 = 2412 那麼這個key就被分配到了節點A上 。


同樣的,當我連接(A,B,C)的任意一個節點想獲取my_name這個key,都會轉到節點A上 ,再比如 ,如果用戶將新節點 D 添加到集群中, 那麼集群只需要將節點 A 、B 、 C 中的某些槽移動到節點 D 就可以了。 


增加一個D節點的結果可能如下: 

  • 節點A改寫1365-5460 

  • 節點B改寫6827-10922 

  • 節點C改寫12288-16383 

  • 節點D改寫0-1364,5461-6826,10923-1228


與此類似, 如果用戶要從集群中移除節點 A , 那麼集群只需要將節點 A 中的所有哈希槽移動到節點 B 和節點 C , 然後再移除空白(不包含任何哈希槽)的節點 A 就可以了。 


因為將一個哈希槽從一個節點移動到另一個節點不會造成節點阻塞, 所以無論是添加新節點還是移除已存在節點, 又或者改變某個節點包含的哈希槽數量, 都不會造成集群下線。 


所以,Redis Cluster的模型大概是這樣的形狀 


主從複製模型

為了使得集群在一部分節點下線或者無法與集群的大多數(majority)節點進行通訊的情況下, 仍然可以正常運作, Redis 集群對節點使用了主從複製功能: 集群中的每個節點都有 1 個至 N 個複製品(replica), 其中一個複製品為主節點(master), 而其餘的 N-1 個複製品為從節點(slave)。


在之前列舉的節點 A 、B 、C 的例子中, 如果節點 B 下線了, 那麼集群將無法正常運行, 因為集群找不到節點來處理 5501 號至 11000號的哈希槽。


另一方面, 假如在創建集群的時候(或者至少在節點 B 下線之前), 我們為主節點 B 添加了從節點 B1 , 那麼當主節點 B 下線的時候, 集群就會將 B1 設置為新的主節點, 並讓它代替下線的主節點 B , 繼續處理 5501 號至 11000 號的哈希槽, 這樣集群就不會因為主節點 B 的下線而無法正常運作了。


不過如果節點 B 和 B1 都下線的話, Redis 集群還是會停止運作。


Redis一致性保證

Redis 並不能保證資料的強一致性. 這意味這在實際中集群在特定的條件下可能會丟失寫操作: 


第一個原因是因為集群是用了異步複製. 寫操作過程: 

  1. 客戶端向主節點B寫入一條命令. 

  2. 主節點B向客戶端回覆命令狀態. 

  3. 主節點將寫操作複製給他得從節點 B1, B2 和 B3


主節點對命令的複製工作發生在傳回命令回覆之後, 因為如果每次處理命令請求都需要等待複製操作完成的話, 那麼主節點處理命令請求的速度將極大地降低 —— 我們必須在性能和一致性之間做出權衡。 


註意:Redis 集群可能會在將來提供同步寫的方法。 Redis 集群另外一種可能會丟失命令的情況是集群出現了網絡分割槽, 並且一個客戶端與至少包括一個主節點在內的少數實體被孤立。 


舉個例子 假設集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六個節點, 其中 A 、B 、C 為主節點, A1 、B1 、C1 為A,B,C的從節點, 還有一個客戶端 Z1 假設集群中發生網絡分割槽,那麼集群可能會分為兩方,大部分的一方包含節點 A 、C 、A1 、B1 和 C1 ,小部分的一方則包含節點 B 和客戶端 Z1 . 


Z1仍然能夠向主節點B中寫入, 如果網絡分割槽發生時間較短,那麼集群將會繼續正常運作,如果分割槽的時間足夠讓大部分的一方將B1選舉為新的master,那麼Z1寫入B中得資料便丟失了. 


註意, 在網絡分裂出現期間, 客戶端 Z1 可以向主節點 B 發送寫命令的最大時間是有限制的, 這一時間限制稱為節點超時時間(node timeout), 是 Redis 集群的一個重要的配置選項


搭建Redis集群

要讓集群正常工作至少需要3個主節點,在這裡我們要創建6個redis節點,其中三個為主節點,三個為從節點,對應的redis節點的ip和端口對應關係如下(為了簡單演示都在同一臺機器上面)

127.0.0.1:7000
127.0.0.1:7001
127.0.0.1:7002
127.0.0.1:7003
127.0.0.1:7004
127.0.0.1:7005


安裝和啟動Redis

下載安裝包

wget http://download.redis.io/releases/redis-3.2.9.tar.gz


解壓安裝

tar zxvf redis-3.2.9.tar.gz
cd redis-3.2.9
make && make PREFIX=/usr/local/redis install


這裡如果失敗的自行yum安裝gcc和tcl 

yum install gcc 
yum install tcl


創建目錄

cd /usr/local/redis
mkdir cluster
cd cluster
mkdir 7000 7001 7002 7003 7004 7005


複製和修改配置檔案

將redis目錄下的配置檔案複製到對應端口檔案夾下,6個檔案夾都要複製一份

cp redis-3.2.9/redis.conf /usr/local/redis/cluster/7000


修改配置檔案redis.conf,將下麵的選項修改

# 端口號
port 7000
# 後臺啟動
daemonize yes
# 開啟集群
cluster-enabled yes
#集群節點配置檔案
cluster-config-file nodes-7000.conf
# 集群連接超時時間
cluster-node-timeout 5000
# 行程pid的檔案位置
pidfile /var/run/redis-7000.pid
# 開啟aof
appendonly yes
# aof檔案路徑
appendfilename "appendonly-7005.aof"
# rdb檔案路徑
dbfilename dump-7000.rdb

6個配置檔案安裝對應的端口分別修改配置檔案


創建啟動腳本

在/usr/local/redis目錄下創建一個start.sh

#!/bin/bash
bin/redis-server cluster/7000/redis.conf
bin/redis-server cluster/7001/redis.conf
bin/redis-server cluster/7002/redis.conf
bin/redis-server cluster/7003/redis.conf
bin/redis-server cluster/7004/redis.conf
bin/redis-server cluster/7005/redis.conf


這個時候我們查看一下行程看啟動情況

ps -ef | grep redis


行程狀態如下:

root      1731     1  1 18:21 ?        00:00:49 bin/redis-server *:7000 [cluster]       
root      1733     1  0 18:21 ?        00:00:29 bin/redis-server *:7001 [cluster]       
root      1735     1  0 18:21 ?        00:00:08 bin/redis-server *:7002 [cluster]       
root      1743     1  0 18:21 ?        00:00:26 bin/redis-server *:7003 [cluster]       
root      1745     1  0 18:21 ?        00:00:13 bin/redis-server *:7004 [cluster]       
root      1749     1  0 18:21 ?        00:00:08 bin/redis-server *:7005 [cluster]

有6個redis行程在開啟,說明我們的redis就啟動成功了


開啟集群

這裡我們只是開啟了6個redis行程而已,它們都還只是獨立的狀態,還麽有組成集群 

這裡我們使用官方提供的工具redis-trib,不過這個工具是用ruby寫的,要先安裝ruby的環境

yum install ruby rubygems -y


執行,報錯

[root@centos]# redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- redis (LoadError)
   from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'

   from /usr/local/bin/redis-trib.rb:25
[root@centos]#

原來是ruby和redis的連接沒安裝好 


安裝gem-redis

gem install redis


安裝到這裡的時候突然卡住很久不動,網上搜了下,這裡需要翻牆或者換鏡像

gem source -a https://gems.ruby-china.org


這裡可以將鏡像換成ruby-china的鏡像,不過我好像更換失敗,最終還是翻牆下載了

[root@centos]gem install redis
Successfully installed redis-3.2.1
gem installed
Installing ri documentation for redis-3.2.1...
Installing RDoc documentation for redis-3.2.1...


等下載好後我們就可以使用了

[root@centos]gem install redis
Successfully installed redis-3.2.1
gem installed
Installing ri documentation for redis-3.2.1...
Installing RDoc documentation for redis-3.2.1...


將redis-3.2.9的src目錄下的trib複製到相應檔案夾

cp redis-3.2.9/src/redis-trib.rb /usr/local/redis/bin/redis-trib


創建集群:

redis-trib create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005


命令的意義如下: 

  • 給定 redis-trib.rb 程式的命令是 create , 這表示我們希望創建一個新的集群。 

  • 選項 –replicas 1 表示我們希望為集群中的每個主節點創建一個從節點。 

之後跟著的其他引數則是實體的地址串列, 我們希望程式使用這些地址所指示的實體來創建新集群。 


簡單來說,以上的命令的意思就是讓redis-trib程式幫我們創建三個主節點和三個從節點的集群,


接著, redis-trib 會打印出一份預想中的配置給你看, 如果你覺得沒問題的話, 就可以輸入 yes , redis-trib 就會將這份配置應用到集群當中:

>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:7000
127.0.0.1:7001
127.0.0.1:7002
Adding replica 127.0.0.1:7003 to 127.0.0.1:7000
Adding replica 127.0.0.1:7004 to 127.0.0.1:7001
Adding replica 127.0.0.1:7005 to 127.0.0.1:7002
Mbdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000
  slots:0-5460 (5461 slots) master
Mb85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001
  slots:5461-10922 (5462 slots) master
Mb681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002
  slots:10923-16383 (5461 slots) master
Sd403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003
  replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446a
Sb7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004
  replicates b85519795fa42aa33d4e88d25104cbae895933a6
S8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005
  replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6
Can I set the above configuration? (type 'yes' to accept):


按下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 127.0.0.1:7000)
Mbdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000
  slots:0-5460 (5461 slotsmaster
  1 additional replica(s)
Sd403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003
  slots: (0 slotsslave
  replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446a
S: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005
  slots: (0 slotsslave
  replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6
Mb85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001
  slots:5461-10922 (5462 slotsmaster
  1 additional replica(s)
Sb7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004
  slots: (0 slotsslave
  replicates b85519795fa42aa33d4e88d25104cbae895933a6
Mb681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002
  slots:10923-16383 (5461 slotsmaster
  1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.


Redis集群的使用

連接集群

這裡我們使用reids-cli連接集群,使用時加上-c引數,就可以連接到集群 

連接7000端口的節點

[root@centos1 redis]# ./redis-cli -c -p 7000
127.0.0.1:7000> set name zgj
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get name
"zgj"


前面的理論知識我們知道了,分配key的時候,它會使用CRC16演算法,這裡將keyname分配到了7001節點上

Redirected to slot [5798] located at 127.0.0.1:7001

redis cluster 採用的方式很直接,它直接跳轉到7001 節點了,而不是還在自身的7000節點。


好,現在我們連接7003這個從節點進入

[root@centos1 redis]# ./redis-cli -c -p 7003
127.0.0.1:7003> get name
-> Redirected to slot [5798] located at 127.0.0.1:7001
"zgj"


這裡獲取name的值,也同樣跳轉到了7001上 

我們再測試一下其他資料

127.0.0.1:7001set age 20
-Redirected to slot [741] located at 127.0.0.1:7000
OK
127.0.0.1:7000set message helloworld
-Redirected to slot [11537] located at 127.0.0.1:7002
OK
127.0.0.1:7002set height 175
-Redirected to slot [8223] located at 127.0.0.1:7001
OK

我們發現資料會在7000-7002這3個節點之間來回跳轉


測試集群中的節點掛掉

上面我們建立了一個集群,3個主節點和3個從節點,7000-7002負責存取資料,7003-7005負責把7000-7005的資料同步到自己的節點上來。 


我們現在來模擬一下一臺matser服務器宕機的情況

[root@centos1 redis]ps -ef | grep redis
root      1731     1  0 18:21 ?        00:01:02 bin/redis-server *:7000 [cluster]       
root      1733     1  0 18:21 ?        00:00:43 bin/redis-server *:7001 [cluster]       
root      1735     1  0 18:21 ?        00:00:22 bin/redis-server *:7002 [cluster]       
root      1743     1  0 18:21 ?        00:00:40 bin/redis-server *:7003 [cluster]       
root      1745     1  0 18:21 ?        00:00:27 bin/redis-server *:7004 [cluster]       
root      1749     1  0 18:21 ?        00:00:22 bin/redis-server *:7005 [cluster]       
root     23988     1  0 18:30 ?        00:00:42 ./redis-server *:6379    
root     24491  1635  0 21:55 pts/1    00:00:00 grep redis
[root@centos1 redis]kill 1731
[root@centos1 redis]bin/redis-trib check 127.0.0.1:7001
>>> Performing Cluster Check (using node 127.0.0.1:7001)
Mb85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001
  slots:5461-10922 (5462 slots) master
  1 additional replica(s)
Mb681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002
  slots:10923-16383 (5461 slots) master
  1 additional replica(s)
Sb7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004
  slots: (0 slots) slave
  replicates b85519795fa42aa33d4e88d25104cbae895933a6
S8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005
  slots: (0 slots) slave
  replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6
Md403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003
  slots:0-5460 (5461 slots) master
  0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

這裡看得出來,現在已經有了3個節點了,7003被選取成了替代7000成為主節點了。 


我們再來模擬 7000節點重新啟動了的情況,那麼它還會自動加入到集群中嗎?那麼,7000這個節點上充當什麼角色呢? 我們試一下:

[root@centos1 redis]bin/redis-server cluster/7000/redis.conf
[root@centos1 redis]bin/redis-trib check 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
Sbdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000
  slots: (0 slots) slave
  replicates d403713ab9db48aeac5b5393b69e1201026ef479
Sb7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004
  slots: (0 slots) slave
  replicates b85519795fa42aa33d4e88d25104cbae895933a6
S8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005
  slots: (0 slots) slave
  replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6
Mb681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002
  slots:10923-16383 (5461 slots) master
  1 additional replica(s)
Mb85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001
  slots:5461-10922 (5462 slots) master
  1 additional replica(s)
Md403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003
  slots:0-5460 (5461 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.

這裡我們可以看到7000節點變成了7003節點的從節點 


我們試著將7000和7003兩個節點都關掉

[root@centos1 redis]ps -ef | grep redis
root      1733     1  0 18:21 ?        00:00:45 bin/redis-server *:7001 [cluster]       
root      1735     1  0 18:21 ?        00:00:24 bin/redis-server *:7002 [cluster]       
root      1743     1  0 18:21 ?        00:00:42 bin/redis-server *:7003 [cluster]       
root      1745     1  0 18:21 ?        00:00:29 bin/redis-server *:7004 [cluster]       
root      1749     1  0 18:21 ?        00:00:24 bin/redis-server *:7005 [cluster]       
root     23988     1  0 18:30 ?        00:00:43 ./redis-server *:6379    
root     24527     1  0 22:04 ?        00:00:00 bin/redis-server *:7000 [cluster]       
root     24541  1635  0 22:07 pts/1    00:00:00 grep redis
[root@centos1 redis] kill 1743
[root@centos1 redis] kill 24527

[root@centos1 redis]bin/redis-trib check 127.0.0.1:7001
>>> Performing Cluster Check (using node 127.0.0.1:7001)
Mb85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001
  slots:5461-10922 (5462 slots) master
  1 additional replica(s)
Mb681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002
  slots:10923-16383 (5461 slots) master
  1 additional replica(s)
Sb7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004
  slots: (0 slots) slave
  replicates b85519795fa42aa33d4e88d25104cbae895933a6
S8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005
  slots: (0 slots) slave
  replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[ERR] Not all 16384 slots are covered by nodes.

這裡我們的集群就不能工作了,因為兩個節點主節點和從節點都掛掉了,原來7001分配的slot現在無節點接管,需要人工介入重新分配slots。


集群中加入新的主節點

這裡在cluster目錄下再新建一個7006並修改對應的配置檔案,然後啟動這個這個redis行程 


然後再使用redis-trib的add node指令加入節點

bin/redis-trib add-node 127.0.0.1:7006 127.0.0.1:7000


這裡前面的節點表示要加入的節點,第二個節點表示要加入的集群中的任意一個節點,用來標識這個集群

[root@centos1 redis]bin/redis-trib add-node 127.0.0.1:7006 127.0.0.1:7000
>>> Adding node 127.0.0.1:7006 to cluster 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
Mbdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000
  slots:0-5460 (5461 slots) master
  1 additional replica(s)
Sd403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003
  slots: (0 slots) slave
  replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446a
Sb7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004
  slots: (0 slots) slave
  replicates b85519795fa42aa33d4e88d25104cbae895933a6
Mb85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001
  slots:5461-10922 (5462 slots) master
  1 additional replica(s)
S8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005
  slots: (0 slots) slave
  replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6
Mb681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002
  slots:10923-16383 (5461 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.
>>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster.
[OK] New node added correctly.
[root@centos1 redis]bin/redis-trib check 127.0.0.1:7006
>>> Performing Cluster Check (using node 127.0.0.1:7000)
Mbdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000
  slots:0-5460 (5461 slots) master
  1 additional replica(s)
Sd403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003
  slots: (0 slots) slave
  replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446a
Sb7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004
  slots: (0 slots) slave
  replicates b85519795fa42aa33d4e88d25104cbae895933a6
Me55599320dabfb31bd22a01407e66121f075e7d3 127.0.0.1:7006
  slots: (0 slots) master
  0 additional replica(s)
Mb85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001
  slots:5461-10922 (5462 slots) master
  1 additional replica(s)
S8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005
  slots: (0 slots) slave
  replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6
Mb681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002
  slots:10923-16383 (5461 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.


這裡我們可以看到7006節點已經變成了一個主節點,然鵝,等等,好像發現了有什麼地方不對

Me55599320dabfb31bd22a01407e66121f075e7d3 127.0.0.1:7006
  slots: (0 slotsmaster

裡面0 slots,也就是說節點6沒有分配哈希槽,即不能進行資料的存取,拿我加上去幹嘛。


原來redis cluster 不是在新加節點的時候幫我們做好了遷移工作,需要我們手動對集群進行重新分片遷移,也是這個命令:

/bin/redis-trib reshard 127.0.0.1:7000


這個命令是用來遷移slot節點的,後面的127.0.0.1:7000是表示哪個集群的,7000-7006都是可以的

[root@centos1]redis-trib.rb reshard 127.0.0.1:7000
Connecting to node 127.0.0.1:7006OK
Connecting to node 127.0.0.1:7001OK
Connecting to node 127.0.0.1:7004OK
Connecting to node 127.0.0.1:7000OK
Connecting to node 127.0.0.1:7002OK
Connecting to node 127.0.0.1:7005OK
Connecting to node 127.0.0.1:7003OK
>>> Performing Cluster Check (using node 127.0.0.1:7006)
Mefc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7006
  slots: (0 slotsmaster
  0 additional replica(s)
Mcb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001
  slots:5461-10922 (5462 slotsmaster
  1 additional replica(s)
S: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004
  slots: (0 slotsslave
  replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c
S: 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000
  slots: (0 slotsslave
  replicates d2237fdcfbba672de766b913d1186cebcb6e1761
Mdfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002
  slots:10923-16383 (5461 slotsmaster
  1 additional replica(s)
S: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005
  slots: (0 slotsslave
  replicates dfa0754c7854a874a6ebd2613b86140ad97701fc
Md2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003
  slots:0-5460 (5461 slotsmaster
  1 additional replica(s)
[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)?

它提示我們需要遷移多少slot到7006上,我們可以算一下:16384/4 = 4096,也就是說,為了平衡分配起見,我們需要移動4096個槽點到7006上。


好,那輸入4096: 

它又提示我們,接受的node ID是多少,7006的id 我們通過上面就可以看到是efc3131fbdc6cf929720e0e0f7136cae85657481:

What is the receiving node ID? efc3131fbdc6cf929720e0e0f7136cae85657481
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:

接著, redis-trib 會向你詢問重新分片的源節點(source node), 也即是, 要從哪個節點中取出 4096 個哈希槽, 並將這些槽移動到7006節點上面。


如果我們不打算從特定的節點上取出指定數量的哈希槽, 那麼可以向 redis-trib 輸入 all , 這樣的話, 集群中的所有主節點都會成為源節點, redis-trib 將從各個源節點中各取出一部分哈希槽, 湊夠 4096 個, 然後移動到7006節點上:

Source node #1:all


接下來就開始遷移了,並且會詢問你是否確認:

Moving slot 1359 from d2237fdcfbba672de766b913d1186cebcb6e1761
   Moving slot 1360 from d2237fdcfbba672de766b913d1186cebcb6e1761
   Moving slot 1361 from d2237fdcfbba672de766b913d1186cebcb6e1761
   Moving slot 1362 from d2237fdcfbba672de766b913d1186cebcb6e1761
   Moving slot 1363 from d2237fdcfbba672de766b913d1186cebcb6e1761
   Moving slot 1364 from d2237fdcfbba672de766b913d1186cebcb6e1761
Do you want to proceed with the proposed reshard plan (yes/no)?

輸入yes並回車後,redis-trib就會正式執行重新分片操作,將制定的哈希槽從源節點一個個移動到7006節點上 。


遷移結束之後,我們來檢查一下

Mbdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000
  slots:1365-5460 (4096 slotsmaster
  1 additional replica(s)
Sd403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003
  slots: (0 slotsslave
  replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446a
Sb7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004
  slots: (0 slotsslave
  replicates b85519795fa42aa33d4e88d25104cbae895933a6
Me55599320dabfb31bd22a01407e66121f075e7d3 127.0.0.1:7006
  slots:0-1364,5461-6826,10923-12287 (4096 slotsmaster
  0 additional replica(s)
Mb85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001
  slots:6827-10922 (4096 slotsmaster
  1 additional replica(s)
S: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005
  slots: (0 slotsslave
  replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6
Mb681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002
  slots:12288-16383 (4096 slotsmaster
  1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

我們可以看到 

slots:0-1364,5461-6826,10923-12287 (4096 slots) 

這些原來在其他節點上的哈希槽都遷移到了7006上


增加一個從節點

新建一個 7007從節點,作為7006的從節點


我們再新建一個節點7007,步驟類似,就先省略了。建好後,啟動起來,我們看如何把它加入到集群中的從節點中:

[root@centos1]redis-trib.rb add-node --slave 127.0.0.1:7007 127.0.0.1:7000

add-node的時候加上–slave表示是加入到從節點中,但是這樣加,是隨機的。這裡的命令列完全像我們在添加一個新主服務器時使用的一樣,所以我們沒有指定要給哪個主服 務器添加副本。這種情況下,redis-trib會將7007作為一個具有較少副本的隨機的主服務器的副本。


那麼,你猜,它會作為誰的從節點,應該是7006,因為7006還沒有從節點。我們運行下。

[root@web3 7007]redis-trib.rb add-node --slave 127.0.0.1:7007 127.0.0.1:7000
...
...
[OK] All 16384 slots covered.
Automatically selected master 127.0.0.1:7006
Connecting to node 127.0.0.1:7007OK
>>> Send CLUSTER MEET to node 127.0.0.1:7007 to make it join the cluster.
Waiting for the cluster to join.
>>> Configure node as replica of 127.0.0.1:7006.
[OK] New node added correctly.


上面提示說,自動選擇了7006作為master節點。並且成功了。我們檢查下:

[root@centos1]redis-trib.rb check 127.0.0.1:7000
Connecting to node 127.0.0.1:7000OK
Connecting to node 127.0.0.1:7006OK
Connecting to node 127.0.0.1:7004OK
Connecting to node 127.0.0.1:7005OK
Connecting to node 127.0.0.1:7003OK
Connecting to node 127.0.0.1:7001OK
Connecting to node 127.0.0.1:7007OK
Connecting to node 127.0.0.1:7002OK
>>> Performing Cluster Check (using node 127.0.0.1:7000)
S: 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000
  slots: (0 slotsslave
  replicates d2237fdcfbba672de766b913d1186cebcb6e1761
Mefc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7006
  slots:0-1364,5461-6826,10923-12287 (4096 slotsmaster
  1 additional replica(s)
S: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004
  slots: (0 slotsslave
  replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c
S: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005
  slots: (0 slotsslave
  replicates dfa0754c7854a874a6ebd2613b86140ad97701fc
Md2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003
  slots:1365-5460 (4096 slotsmaster
  1 additional replica(s)
Mcb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001
  slots:6827-10922 (4096 slotsmaster
  1 additional replica(s)
S: 86d05e7c2b197dc182b5e71069e791d033cf899e 127.0.0.1:7007
  slots: (0 slotsslave
  replicates efc3131fbdc6cf929720e0e0f7136cae85657481
Mdfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002
  slots:12288-16383 (4096 slotsmaster
  1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

果然,7007加入到了7006的從節點當中。


你說,我如果想指定一個主節點行不行?當然可以。我們再建一個7008節點。

bin/redis-trib.rb add-node --slave --master-id efc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7008 127.0.0.1:7000


–master-id 表示指定的主節點node id。這裡指定的是 7006 這個主節點。

Waiting for the cluster to join.
>>> Configure node as replica of 127.0.0.1:7006.
[OK] New node added correctly.


提示我們已經作為7006的從節點了,也就是加入到7006的從節點來了,照這麼說,7006就有2個從節點了,我們看一下:

bin/redis-cli -c -p 7008 cluster nodes |grep efc3131fbdc6cf929720e0e0f7136cae85657481
86d05e7c2b197dc182b5e71069e791d033cf899e 127.0.0.1:7007 slave efc3131fbdc6cf929720e0e0f7136cae85657481 0 1445089507786 8 connected
efc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7006 master - 0 1445089508289 8 connected 0-1364 5461-6826 10923-12287
44321e7d619410dc4e0a8745366610a0d06d2395 127.0.0.1:7008 myself,slave efc3131fbdc6cf929720e0e0f7136cae85657481 0 0 0 connected

我們過濾了下看結果,果真,7007和7008是7006的從節點了。


剛好,我們再做一個實驗,我把7006的行程殺掉,看7007和7008誰會變成主節點:

[root@centos1]# ps -ef|grep redis
root     11384     1  0 09:56 ?        00:00:16 redis-server *:7001 [cluster]
root     11388     1  0 09:56 ?        00:00:16 redis-server *:7002 [cluster]
root     11392     1  0 09:56 ?        00:00:16 redis-server *:7003 [cluster]
root     11396     1  0 09:56 ?        00:00:15 redis-server *:7004 [cluster]
root     11400     1  0 09:56 ?        00:00:15 redis-server *:7005 [cluster]
root     12100     1  0 11:01 ?        00:00:11 redis-server *:7000 [cluster]
root     12132     1  0 11:28 ?        00:00:11 redis-server *:7006 [cluster]
root     12202     1  0 13:14 ?        00:00:02 redis-server *:7007 [cluster]
root     12219     1  0 13:39 ?        00:00:00 redis-server *:7008 [cluster]
root     12239  8259  0 13:49 pts/0    00:00:00 grep redis
[root@centos1]# kill 12132
[root@centos1]# redis-cli -c -p 7008
127.0.0.1:7008> get ss5rtr
-> Redirected to slot [1188] located at 127.0.0.1:7007
"66"
127.0.0.1:7007> cluster nodes
efc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7006 master,fail - 1445089780668 1445089779963 8 disconnected
d2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003 master - 0 1445089812195 7 connected 1365-5460
30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 slave dfa0754c7854a874a6ebd2613b86140ad97701fc 0 1445089813710 3 connected
86d05e7c2b197dc182b5e71069e791d033cf899e 127.0.0.1:7007 myself,master - 0 0 10 connected 0-1364 5461-6826 10923-12287
cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 master - 0 1445089814214 2 connected 6827-10922
4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 slave cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 0 1445089812701 2 connected
44321e7d619410dc4e0a8745366610a0d06d2395 127.0.0.1:7008 slave 86d05e7c2b197dc182b5e71069e791d033cf899e 0 1445089814214 10 connected
3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000 slave d2237fdcfbba672de766b913d1186cebcb6e1761 0 1445089813204 7 connected
dfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 master - 0 1445089813204 3 connected 12288-16383
127.0.0.1:7007>

這裡7007獲得了成為主節點的機會,7008就變成了7007的從節點。


那麼這個時候,重啟7006節點,那麼他就會變成了一個7007的從節點了。


移除一個節點

上面是增加一個節點,接下來就是移除一個節點了,移除節點的命令是

bin/redis-trib del-node 127.0.0.1:7000 ``


沒我們嘗試下輸入以下命令

[root@centos]bin/redis-trib.rb del-node 127.0.0.1:7000 86d05e7c2b197dc182b5e71069e791d033cf899e
>>> Removing node 86d05e7c2b197dc182b5e71069e791d033cf899e from cluster 127.0.0.1:7000
Connecting to node 127.0.0.1:7000OK
Connecting to node 127.0.0.1:7006OK
Connecting to node 127.0.0.1:7004OK
Connecting to node 127.0.0.1:7001OK
Connecting to node 127.0.0.1:7003OK
Connecting to node 127.0.0.1:7007OK
Connecting to node 127.0.0.1:7008OK
Connecting to node 127.0.0.1:7005OK
Connecting to node 127.0.0.1:7002OK
[ERR] Node 127.0.0.1:7007 is not emptyReshard data away and try again.


這裡報錯了,提示我們7007節點裡面有資料,讓我們把7007節點里的資料移除出去,也就是說需要重新分片,這個和上面增加節點的方式一樣,我們再來一遍

bin/redis-trib.rb reshard 127.0.0.1:7000

省去中間內容,原來7007節點上已經有了4096個哈希槽,這裡我們也移動4096個哈希槽 


然後將這些哈希槽移動到7001節點上

Source node #1:86d05e7c2b197dc182b5e71069e791d033cf899e
Source node #2:done
Do you want to proceed with the proposed reshard plan (yes/no)? yes


然後我們再繼續執行移除命令,結果如下

[root@centos1]redis-trib.rb del-node 127.0.0.1:7000 86d05e7c2b197dc182b5e71069e791d033cf899e
>>> Removing node 86d05e7c2b197dc182b5e71069e791d033cf899e from cluster 127.0.0.1:7000
Connecting to node 127.0.0.1:7000OK
Connecting to node 127.0.0.1:7006OK
Connecting to node 127.0.0.1:7004OK
Connecting to node 127.0.0.1:7001OK
Connecting to node 127.0.0.1:7003OK
Connecting to node 127.0.0.1:7007OK
Connecting to node 127.0.0.1:7008OK
Connecting to node 127.0.0.1:7005OK
Connecting to node 127.0.0.1:7002OK
>>> Sending CLUSTER FORGET messages to the cluster...
>>> 127.0.0.1:7006 as replica of 127.0.0.1:7001
>>> 127.0.0.1:7008 as replica of 127.0.0.1:7001
>>> SHUTDOWN the node.

刪除成功,而且還很人性化的將7006和7008這2個原來7007的附屬節點送給了7001。考慮的真周到~


移除一個從節點

移除一個從節點就比較簡單了,因為從節點沒有哈希槽,也不需要考慮資料遷移,直接移除就行

[root@centos1]# redis-trib.rb del-node 127.0.0.1:7005 44321e7d619410dc4e0a8745366610a0d06d2395
>>> Removing node 44321e7d619410dc4e0a8745366610a0d06d2395 from cluster 127.0.0.1:7005
Connecting to node 127.0.0.1:7005: OK
Connecting to node 127.0.0.1:7001: OK
Connecting to node 127.0.0.1:7002: OK
Connecting to node 127.0.0.1:7004: OK
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7006: OK
Connecting to node 127.0.0.1:7008: OK
Connecting to node 127.0.0.1:7003: OK
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
[root@centos1]# redis-trib.rb check 127.0.0.1:7008
Connecting to node 127.0.0.1:7008: [ERR] Sorry, can't connect to node 127.0.0.1:7008

表示移除成功


Redis性能測試

Redis自帶了性能測試工具redis-benchmark 

使用說明如下:

Usage: redis-benchmark [-h ] [-p ] [-c ] [-n  [-k ]

-h       Server hostname (default 127.0.0.1)
-p           Server port (default 6379)
-s         Server socket (overrides host and port)
-c        Number of parallel connections (default 50)
-n       Total number of requests (default 10000)
-d           Data size of SET/GET value in bytes (default 2)
-k        1=keep alive 0=reconnect (default 1)
-r    Use random keys for SET/GET/INCR, random values for SADD
 Using this option the benchmark will get/set keys
 in the form mykey_rand:000000012456 instead of constant
 keys, the  argument determines the max
 number of values for the random number. For instance
 if set to 10 only rand:000000000000 - rand:000000000009
 range will be allowed.
-P         Pipeline  requests. Default 1 (no pipeline).
-q                 Quiet. Just show query/sec values
--csv              Output in CSV format
-l                 Loop. Run the tests forever
-t          Only run the comma-separated list of tests. The test
                   names are the same as the ones produced as output.
-I                 Idle mode. Just open N idle connections and wait.


基準測試

基準的測試命令: 

redis-benchmark -q -n 100000 

結果入下:

root@centos1 bin]# redis-benchmark -q -n 100000
-bash: redis-benchmark: command not found
[root@centos1 bin]# ./redis-benchmark -q -n 100000
PING_INLINE: 61576.36 requests per second
PING_BULK: 60277.28 requests per second
SET: 61349.69 requests per second
GET: 60459.49 requests per second
INCR: 58858.15 requests per second
LPUSH: 59066.75 requests per second
RPUSH: 57339.45 requests per second
LPOP: 55586.44 requests per second
RPOP: 56465.27 requests per second
SADD: 57045.07 requests per second
SPOP: 53734.55 requests per second
LPUSH (needed to benchmark LRANGE): 57012.54 requests per second
LRANGE_100 (first 100 elements): 55803.57 requests per second
LRANGE_300 (first 300 elements): 54914.88 requests per second
LRANGE_500 (first 450 elements): 53333.33 requests per second
LRANGE_600 (first 600 elements): 56529.11 requests per second
MSET (10 keys): 59276.82 requests per second

這裡可以看出,單機版的redis每秒可以處理6萬個請求,這已經是一個非常厲害的資料了,不得不佩服 


我們再來看下集群情況下是是什麼情況

[root@centos1 bin]# ./redis-benchmark -q -n 100000 -p 7000
PING_INLINE64599.48 requests per second
PING_BULK64184.85 requests per second
SET66800.27 requests per second
GET65616.80 requests per second
INCR66269.05 requests per second
LPUSH40273.86 requests per second
RPUSH40355.12 requests per second
LPOP43421.62 requests per second
RPOP45187.53 requests per second
SADD62539.09 requests per second
SPOP61538.46 requests per second
LPUSH (needed to benchmark LRANGE): 38182.51 requests per second
LRANGE_100 (first 100 elements): 25555.84 requests per second
LRANGE_300 (first 300 elements): 9571.21 requests per second
LRANGE_500 (first 450 elements): 7214.49 requests per second
LRANGE_600 (first 600 elements): 5478.85 requests per second
MSET (10 keys): 41893.59 requests per second

這裡看出大部分和單機版的性能查不多,主要是lrange命令的差別是很大的


流水線測試

使用流水線 

預設情況下,每個客戶端都是在一個請求完成之後才發送下一個請求(基準會模擬50個客戶端除非使用-c指定特別的數量),這意味著服務器幾乎是按順序讀取每個客戶端的命令。RTT也加入了其中。 


真實世界會更複雜,Redis支持/topics/pipelining,使得可以一次性執行多條命令成為可能。Redis流水線可以提高服務器的TPS 

redis-benchmark -n 1000000 -t set,get -P 16 -q 加入-P選項使用管道技術,一次執行多條命令

./redis-benchmark -n 1000000 -t set,get -P 16 -q
SET515198.34 requests per second
GET613873.56 requests per second

每秒處理get/sret請求達到了60/50W,真的厲害!


遇到的問題

安裝redis集群的時候遇到了挺多問題,踩了很多坑,單單是修改配置檔案就出了不少問題,那些配置檔案的內容都要一一修改,有些配置不修改就會出現無法創建行程的錯誤


註意配置集群的時候不要加密碼,否則會出現無法連接的情況

gem install的時候需要修改鏡像或者翻牆

昨天啟動成功,今天啟動的時候報錯

[ERR] Node 172.168.63.202:7001 is not empty. Either the nodealready knows other nodes (check with CLUSTER NODES) or contains some key in database 0


解決方法: 

  1. 將需要新增的節點下aof、rdb等本地備份檔案刪除; 

  2. 同時將新Node的集群配置檔案刪除,即:刪除你redis.conf裡面cluster-config-file所在的檔案; 

  3. 再次添加新節點如果還是報錯,則登錄新Node,執行bin/redis-cli–h x –p對資料庫進行清除:

127.0.0.1:7001>  flushdb      #清空當前資料庫


總結