經驗之談-Redis集群

前言

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 集群提供了以下兩個好處:

  • 將數據自動切分(split)到多個節點的能力。

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

數據分片

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的模型大概是這樣的形狀

經驗之談-Redis集群替換高清大圖

082248582749859.jpg

主從複製模型

為了使得集群在一部分節點下線或者無法與集群的大多數(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:7000127.0.0.1:7001127.0.0.1:7002127.0.0.1:7003127.0.0.1:7004127.0.0.1:7005

安裝和啟動Redis

下載安裝包

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

解壓安裝

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

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

 yum install gcc yum install tcl

創建目錄

cd /usr/local/redismkdir clustercd clustermkdir 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# 開啟aofappendonly yes# aof文件路徑appendfilename "appendonly-7005.aof"# rdb文件路徑dbfilename dump-7000.rdb

6個配置文件安裝對應的端口分別修改配置文件

創建啟動腳本

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

#!/bin/bashbin/redis-server cluster/7000/redis.confbin/redis-server cluster/7001/redis.confbin/redis-server cluster/7002/redis.confbin/redis-server cluster/7003/redis.confbin/redis-server cluster/7004/redis.confbin/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 redisSuccessfully installed redis-3.2.11 gem installedInstalling ri documentation for redis-3.2.1...Installing RDoc documentation for redis-3.2.1...

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

[root@centos]# gem install redisSuccessfully installed redis-3.2.11 gem installedInstalling 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:7000127.0.0.1:7001127.0.0.1:7002Adding replica 127.0.0.1:7003 to 127.0.0.1:7000Adding replica 127.0.0.1:7004 to 127.0.0.1:7001Adding replica 127.0.0.1:7005 to 127.0.0.1:7002M: bdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000 slots:0-5460 (5461 slots) masterM: b85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001 slots:5461-10922 (5462 slots) masterM: b681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002 slots:10923-16383 (5461 slots) masterS: d403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003 replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446aS: b7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004 replicates b85519795fa42aa33d4e88d25104cbae895933a6S: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005 replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6Can 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 clusterWaiting for the cluster to join...>>> Performing Cluster Check (using node 127.0.0.1:7000)M: bdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000 slots:0-5460 (5461 slots) master 1 additional replica(s)S: d403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003 slots: (0 slots) slave replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446aS: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005 slots: (0 slots) slave replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6M: b85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s)S: b7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004 slots: (0 slots) slave replicates b85519795fa42aa33d4e88d25104cbae895933a6M: b681e1a151890cbf957d1ff08352ee48f6ae39e6 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.

Redis集群的使用

連接集群

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

連接7000端口的節點

[root@centos1 redis]# ./redis-cli -c -p 7000127.0.0.1:7000> set name zgj-> Redirected to slot [5798] located at 127.0.0.1:7001OK127.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 7003127.0.0.1:7003> get name-> Redirected to slot [5798] located at 127.0.0.1:7001"zgj"

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

我們再測試一下其他數據

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

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

測試集群中的節點掛掉

上面我們建立了一個集群,3個主節點和3個從節點,7000-7002負責存取數據,7003-7005負責把7000-7005的數據同步到自己的節點上來。我們現在來模擬一下一臺matser服務器宕機的情況

[root@centos1 redis]# ps -ef | grep redisroot 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)M: b85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s)M: b681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s)S: b7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004 slots: (0 slots) slave replicates b85519795fa42aa33d4e88d25104cbae895933a6S: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005 slots: (0 slots) slave replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6M: d403713ab9db48aeac5b5393b69e1201026ef479 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)S: bdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000 slots: (0 slots) slave replicates d403713ab9db48aeac5b5393b69e1201026ef479S: b7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004 slots: (0 slots) slave replicates b85519795fa42aa33d4e88d25104cbae895933a6S: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005 slots: (0 slots) slave replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6M: b681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s)M: b85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s)M: d403713ab9db48aeac5b5393b69e1201026ef479 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 redisroot 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)M: b85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s)M: b681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s)S: b7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004 slots: (0 slots) slave replicates b85519795fa42aa33d4e88d25104cbae895933a6S: 8a0d2a3f271b349744a971e1b0a545405de2742e 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)M: bdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000 slots:0-5460 (5461 slots) master 1 additional replica(s)S: d403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003 slots: (0 slots) slave replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446aS: b7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004 slots: (0 slots) slave replicates b85519795fa42aa33d4e88d25104cbae895933a6M: b85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s)S: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005 slots: (0 slots) slave replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6M: b681e1a151890cbf957d1ff08352ee48f6ae39e6 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)M: bdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000 slots:0-5460 (5461 slots) master 1 additional replica(s)S: d403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003 slots: (0 slots) slave replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446aS: b7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004 slots: (0 slots) slave replicates b85519795fa42aa33d4e88d25104cbae895933a6M: e55599320dabfb31bd22a01407e66121f075e7d3 127.0.0.1:7006 slots: (0 slots) master 0 additional replica(s)M: b85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s)S: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005 slots: (0 slots) slave replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6M: b681e1a151890cbf957d1ff08352ee48f6ae39e6 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節點已經變成了一個主節點,然鵝,等等,好像發現了有什麼地方不對

M: e55599320dabfb31bd22a01407e66121f075e7d3 127.0.0.1:7006 slots: (0 slots) master

裡面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:7000Connecting to node 127.0.0.1:7006: OKConnecting to node 127.0.0.1:7001: OKConnecting to node 127.0.0.1:7004: OKConnecting to node 127.0.0.1:7000: OKConnecting to node 127.0.0.1:7002: OKConnecting to node 127.0.0.1:7005: OKConnecting to node 127.0.0.1:7003: OK>>> Performing Cluster Check (using node 127.0.0.1:7006)M: efc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7006 slots: (0 slots) master 0 additional replica(s)M: cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s)S: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 slots: (0 slots) slave replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6cS: 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000 slots: (0 slots) slave replicates d2237fdcfbba672de766b913d1186cebcb6e1761M: dfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s)S: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 slots: (0 slots) slave replicates dfa0754c7854a874a6ebd2613b86140ad97701fcM: d2237fdcfbba672de766b913d1186cebcb6e1761 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.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? efc3131fbdc6cf929720e0e0f7136cae85657481Please 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 d2237fdcfbba672de766b913d1186cebcb6e1761Do you want to proceed with the proposed reshard plan (yes/no)?

輸入yes並回車後,redis-trib就會正式執行重新分片操作,將制定的哈希槽從源節點一個個移動到7006節點上遷移結束之後,我們來檢查一下

M: bdcddddd3d78a866b44b68c7ae0e5ccf875c446a 127.0.0.1:7000 slots:1365-5460 (4096 slots) master 1 additional replica(s)S: d403713ab9db48aeac5b5393b69e1201026ef479 127.0.0.1:7003 slots: (0 slots) slave replicates bdcddddd3d78a866b44b68c7ae0e5ccf875c446aS: b7ec92919e5bcffa76c8eee338f8ca5155293c64 127.0.0.1:7004 slots: (0 slots) slave replicates b85519795fa42aa33d4e88d25104cbae895933a6M: e55599320dabfb31bd22a01407e66121f075e7d3 127.0.0.1:7006 slots:0-1364,5461-6826,10923-12287 (4096 slots) master 0 additional replica(s)M: b85519795fa42aa33d4e88d25104cbae895933a6 127.0.0.1:7001 slots:6827-10922 (4096 slots) master 1 additional replica(s)S: 8a0d2a3f271b349744a971e1b0a545405de2742e 127.0.0.1:7005 slots: (0 slots) slave replicates b681e1a151890cbf957d1ff08352ee48f6ae39e6M: b681e1a151890cbf957d1ff08352ee48f6ae39e6 127.0.0.1:7002 slots:12288-16383 (4096 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.

我們可以看到

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:7006Connecting to node 127.0.0.1:7007: OK>>> 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:7000Connecting to node 127.0.0.1:7000: OKConnecting to node 127.0.0.1:7006: OKConnecting to node 127.0.0.1:7004: OKConnecting to node 127.0.0.1:7005: OKConnecting to node 127.0.0.1:7003: OKConnecting to node 127.0.0.1:7001: OKConnecting to node 127.0.0.1:7007: OKConnecting to node 127.0.0.1:7002: OK>>> Performing Cluster Check (using node 127.0.0.1:7000)S: 3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000 slots: (0 slots) slave replicates d2237fdcfbba672de766b913d1186cebcb6e1761M: efc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7006 slots:0-1364,5461-6826,10923-12287 (4096 slots) master 1 additional replica(s)S: 4b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 slots: (0 slots) slave replicates cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6cS: 30858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 slots: (0 slots) slave replicates dfa0754c7854a874a6ebd2613b86140ad97701fcM: d2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003 slots:1365-5460 (4096 slots) master 1 additional replica(s)M: cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 slots:6827-10922 (4096 slots) master 1 additional replica(s)S: 86d05e7c2b197dc182b5e71069e791d033cf899e 127.0.0.1:7007 slots: (0 slots) slave replicates efc3131fbdc6cf929720e0e0f7136cae85657481M: dfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 slots:12288-16383 (4096 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.

果然,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 efc3131fbdc6cf929720e0e0f7136cae8565748186d05e7c2b197dc182b5e71069e791d033cf899e 127.0.0.1:7007 slave efc3131fbdc6cf929720e0e0f7136cae85657481 0 1445089507786 8 connectedefc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7006 master - 0 1445089508289 8 connected 0-1364 5461-6826 10923-1228744321e7d619410dc4e0a8745366610a0d06d2395 127.0.0.1:7008 myself,slave efc3131fbdc6cf929720e0e0f7136cae85657481 0 0 0 connected

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

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

[root@centos1]# ps -ef|grep redisroot 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 7008127.0.0.1:7008> get ss5rtr-> Redirected to slot [1188] located at 127.0.0.1:7007"66"127.0.0.1:7007> cluster nodesefc3131fbdc6cf929720e0e0f7136cae85657481 127.0.0.1:7006 master,fail - 1445089780668 1445089779963 8 disconnectedd2237fdcfbba672de766b913d1186cebcb6e1761 127.0.0.1:7003 master - 0 1445089812195 7 connected 1365-546030858dbf483b61b9838d5c1f853a60beaa4e7afd 127.0.0.1:7005 slave dfa0754c7854a874a6ebd2613b86140ad97701fc 0 1445089813710 3 connected86d05e7c2b197dc182b5e71069e791d033cf899e 127.0.0.1:7007 myself,master - 0 0 10 connected 0-1364 5461-6826 10923-12287cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 127.0.0.1:7001 master - 0 1445089814214 2 connected 6827-109224b4aef8b48c427a3c903518339d53b6447c58b93 127.0.0.1:7004 slave cb5c04b6160c3b7e18cad5d49d8e2987b27e0d6c 0 1445089812701 2 connected44321e7d619410dc4e0a8745366610a0d06d2395 127.0.0.1:7008 slave 86d05e7c2b197dc182b5e71069e791d033cf899e 0 1445089814214 10 connected3707debcbe7be66d4a1968eaf3a5ffaf4308efa4 127.0.0.1:7000 slave d2237fdcfbba672de766b913d1186cebcb6e1761 0 1445089813204 7 connecteddfa0754c7854a874a6ebd2613b86140ad97701fc 127.0.0.1:7002 master - 0 1445089813204 3 connected 12288-16383127.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:7000Connecting to node 127.0.0.1:7000: OKConnecting to node 127.0.0.1:7006: OKConnecting to node 127.0.0.1:7004: OKConnecting to node 127.0.0.1:7001: OKConnecting to node 127.0.0.1:7003: OKConnecting to node 127.0.0.1:7007: OKConnecting to node 127.0.0.1:7008: OKConnecting to node 127.0.0.1:7005: OKConnecting to node 127.0.0.1:7002: OK[ERR] Node 127.0.0.1:7007 is not empty! Reshard data away and try again.

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

bin/redis-trib.rb reshard 127.0.0.1:7000

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

Source node #1:86d05e7c2b197dc182b5e71069e791d033cf899eSource node #2:doneDo 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:7000Connecting to node 127.0.0.1:7000: OKConnecting to node 127.0.0.1:7006: OKConnecting to node 127.0.0.1:7004: OKConnecting to node 127.0.0.1:7001: OKConnecting to node 127.0.0.1:7003: OKConnecting to node 127.0.0.1:7007: OKConnecting to node 127.0.0.1:7008: OKConnecting to node 127.0.0.1:7005: OKConnecting to node 127.0.0.1:7002: OK>>> 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:7005Connecting to node 127.0.0.1:7005: OKConnecting to node 127.0.0.1:7001: OKConnecting to node 127.0.0.1:7002: OKConnecting to node 127.0.0.1:7004: OKConnecting to node 127.0.0.1:7000: OKConnecting to node 127.0.0.1:7006: OKConnecting to node 127.0.0.1:7008: OKConnecting 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:7008Connecting 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 100000PING_INLINE: 61576.36 requests per secondPING_BULK: 60277.28 requests per secondSET: 61349.69 requests per secondGET: 60459.49 requests per secondINCR: 58858.15 requests per secondLPUSH: 59066.75 requests per secondRPUSH: 57339.45 requests per secondLPOP: 55586.44 requests per secondRPOP: 56465.27 requests per secondSADD: 57045.07 requests per secondSPOP: 53734.55 requests per secondLPUSH (needed to benchmark LRANGE): 57012.54 requests per secondLRANGE_100 (first 100 elements): 55803.57 requests per secondLRANGE_300 (first 300 elements): 54914.88 requests per secondLRANGE_500 (first 450 elements): 53333.33 requests per secondLRANGE_600 (first 600 elements): 56529.11 requests per secondMSET (10 keys): 59276.82 requests per second

這裡可以看出,單機版的redis每秒可以處理6萬個請求,這已經是一個非常厲害的數據了,不得不佩服我們再來看下集群情況下是是什麼情況

[root@centos1 bin]# ./redis-benchmark -q -n 100000 -p 7000PING_INLINE: 64599.48 requests per secondPING_BULK: 64184.85 requests per secondSET: 66800.27 requests per secondGET: 65616.80 requests per secondINCR: 66269.05 requests per secondLPUSH: 40273.86 requests per secondRPUSH: 40355.12 requests per secondLPOP: 43421.62 requests per secondRPOP: 45187.53 requests per secondSADD: 62539.09 requests per secondSPOP: 61538.46 requests per secondLPUSH (needed to benchmark LRANGE): 38182.51 requests per secondLRANGE_100 (first 100 elements): 25555.84 requests per secondLRANGE_300 (first 300 elements): 9571.21 requests per secondLRANGE_500 (first 450 elements): 7214.49 requests per secondLRANGE_600 (first 600 elements): 5478.85 requests per secondMSET (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 -qSET: 515198.34 requests per secondGET: 613873.56 requests per second

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

遇到的問題

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

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

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

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

    [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 #清空當前數據庫

總結

之間對了Redis的瞭解並不是說非常多,只是簡單的會用,因為現在企業裡也很多都在用,剛好老大說接下來的項目可能會用到Redis集群,讓我先去了解下,所以最近就在回頭看,一邊看文檔,博客,一邊實踐,踩了很多的坑,出問題的時候的確是讓人感到很痛苦很鬱悶的,可是當運行成功的那一刻心情卻是無比激動和開心的,可能這就是編程的魅力吧。


分享到:


相關文章: