Redis Cluster

  公司以一種錯誤的姿式使用了redis的功能,並且業務框架已經變得很大,雖然你們也都認爲不合理,可是暫時看不到重構但願,可苦逼了後端人員,索性看看redis cluster能不能解決個人顧慮,redis 3出來也一段時間了,網上的文章也不少了,生產上大規模使用的公司很少,並且由於要對cluster的支持,致使不少原有lib庫都沒法使用了,不過這並不妨礙咱們追索知識的步伐....css

Redis很早的時候已經提供了Master-Slave功能,直到3.0版本的時候才提供了cluster功能,仍是alpha版本,cluster並不支持多個鍵的操做,這樣會致使須要在多個節點間移動數據,由於cluster中數據是被自動的分配到多個redis節點的。在cluster中部分節點出現不可用的時候,cluster仍是能夠繼續提供正常服務的(固然要看你的集羣配置)。node

下面聊聊關於redis cluster的一些信息,每一個在cluster中的redis節點都會監聽兩個端口,一個是和客戶端通信的TCP端口(6379),一個是用做cluster內部數據傳輸的,好比錯誤檢測、cluster配置更新、故障轉移等等(16379)。這樣cluster的通信問題就解決了,但數據是怎麼在cluster中存放的?redis cluster沒有采用一致性哈希,而是採用hash slot。一個redis cluster有固定的16384個hash slot,這麼多slot被均勻的分配到cluster中的master節點上,而一個key具體的存儲在哪一個slot上,則是經過key的CRC16編碼對16384取模得出的。上面提到了cluster中的master節點,估計不少人就迷糊了,爲了當部分節點失效時,cluster仍能保持可用,Redis 集羣採用每一個節點擁有 1(主服務自身)到 N 個副本(N-1 個附加的從服務器)的主從模型。是否是和master/slave很像。可是redis cluster卻不是強一致性的,在必定的條件下,cluster可能會丟失一些寫入的請求命令,由於cluster內部master和slave之間的數據是異步複製的,好比你給master中寫入數據,返回ok,可是這時候該數據並不必定就徹底的同步到slave上了(採用異步主要是提升性能,主要是在性能和一致性間的一個平衡),若是這時候master宕機了,這部分沒有寫入slave的數據就丟失了,不過這種可能性仍是很小的。上面這些信息都來自官方解釋,只是大概的描述了redis cluster的一些基本信息。python

其實我的以爲redis以前也有單獨的數據分片功能和主從功能,cluster只是把這些功能合併在一塊兒,外加一些集羣的狀態檢測。下面看看redis cluster的集羣架構圖:nginx

客戶端鏈接在cluster中的任何一個節點上均可以獲取到其餘節點的數據,不過這幅圖看着有點亂,通常cluster內部不會這麼多節點互爲主從的。redis

下面咱們簡單的使用一下,操做系統是一臺ubuntu14.04系統(啓動6個實例,3 Master/3 Slave):sql

1 下載最新版redisshell

 

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

2  編譯安裝ubuntu

 

#安裝到/opt/redis-cluster目錄 tar zxvf redis-3.0.4.tar.gz cd redis-3.0.4/ make make PREFIX=/opt/redis-cluster install #建立配置目錄,拷貝相關文件 mkdir /opt/redis-cluster/{log conf rdb run} cp src/redis-trib.rb /opt/redis-cluster/bin/

3  安裝其餘依賴包後端

 

apt-get install ruby1.9.1 gem install redis

4  建立配置文件ruby

    看看6379的具體信息:grep -v -E '(^#|^$)' redis-6379.conf

 

daemonize yes
pidfile /opt/redis-cluster/run/redis-6379.pid
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
logfile "/opt/redis-cluster/log/redis-6379.log"
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump-6379.rdb dir /opt/redis-cluster/rdb slave-serve-stale-data yes slave-read-only yes repl-diskless-sync no repl-diskless-sync-delay 5 repl-disable-tcp-nodelay no slave-priority 100 appendonly yes appendfilename "redis-6379.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes lua-time-limit 5000 cluster-enabled yes cluster-config-file nodes-6379.conf cluster-node-timeout 15000 slowlog-log-slower-than 10000 slowlog-max-len 128 latency-monitor-threshold 0 notify-keyspace-events "" hash-max-ziplist-entries 512 hash-max-ziplist-value 64 list-max-ziplist-entries 512 list-max-ziplist-value 64 set-max-intset-entries 512 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 hll-sparse-max-bytes 3000 activerehashing yes client-output-buffer-limit normal 0 0 0 client-output-buffer-limit slave 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 hz 10 aof-rewrite-incremental-fsync yes

    其實大部分都是原有的配置信息,不須要作大的改動,與端口相關的都須要調整,與集羣相關的配置以下:

 

cluster-enabled yes
cluster-config-file nodes-6379.conf cluster-node-timeout 15000

    注意其中的nodes-6379.conf這個文件不須要建立,在初始化集羣的時候會自動建立的。

5    在啓動redis實例前先修改一些系統級別的配置

 

echo never > /sys/kernel/mm/transparent_hugepage/enabled # 打開/etc/sysctl.conf,追加以下內容 vm.overcommit_memory = 1 #使配置生效 sysctl -p

6    啓動全部redis實例

 

cd /opt/redis-cluster/bin ./redis-server /opt/redis-cluster/conf/redis-6379.conf ./redis-server /opt/redis-cluster/conf/redis-6380.conf ./redis-server /opt/redis-cluster/conf/redis-6381.conf ./redis-server /opt/redis-cluster/conf/redis-7379.conf ./redis-server /opt/redis-cluster/conf/redis-7380.conf ./redis-server /opt/redis-cluster/conf/redis-7381.conf

7    redis-trib.rb

        redis-trib.rb是一個官方提供的用來操做cluster的ruby腳本,咱們後面管理cluster會常用到這個腳本

 

Usage: redis-trib <command> <options> <arguments ...>  create host1:port1 ... hostN:portN --replicas <arg> check host:port fix host:port reshard host:port --from <arg> --to <arg> --slots <arg> --yes add-node new_host:new_port existing_host:existing_port --slave --master-id <arg> del-node host:port node_id set-timeout host:port milliseconds call host:port command arg arg .. arg import host:port --from <arg> help (show this help) For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.

8    初始化啓動cluster

 

./redis-trib.rb create --replicas 1 172.16.1.100:6379 172.16.1.100:6380 172.16.1.100:6381 172.16.1.100:7379 172.16.1.100:7380 172.16.1.100:7381

      結果以下:

>>> Creating cluster Connecting to node 172.16.1.100:6379: OK Connecting to node 172.16.1.100:6380: OK Connecting to node 172.16.1.100:6381: OK Connecting to node 172.16.1.100:7379: OK Connecting to node 172.16.1.100:7380: OK Connecting to node 172.16.1.100:7381: OK >>> Performing hash slots allocation on 6 nodes... Using 3 masters: ###注意三個6xxx的都被定義爲master的了 172.16.1.100:6379 172.16.1.100:6380 172.16.1.100:6381 Adding replica 172.16.1.100:7379 to 172.16.1.100:6379 ###注意三個7xxx的都被定義爲相關的slave了 Adding replica 172.16.1.100:7380 to 172.16.1.100:6380 Adding replica 172.16.1.100:7381 to 172.16.1.100:6381 M: cdb8b1fe29feb9a564fbfed6599aa61dda250eb1 172.16.1.100:6379 slots:0-5460 (5461 slots) master ###6379被分配了0-5460個slots M: a188b59b30056f61c1cf55ff5072d60b6f8ce5d7 172.16.1.100:6380 slots:5461-10922 (5462 slots) master ###6380被分配了5461-10922個slots M: fada90b0520d5aa3305bc89651cc7bb5de9b2a16 172.16.1.100:6381 slots:10923-16383 (5461 slots) master ###6381被分配了10923-16383個slots S: 818fb192631cf8c34bc58f24d2538444abe7d995 172.16.1.100:7379 ###剩餘的三個slave replicates cdb8b1fe29feb9a564fbfed6599aa61dda250eb1 S: 1cf6ed9f8a5049513fb631e78207c7f0b1ba6674 172.16.1.100:7380 replicates a188b59b30056f61c1cf55ff5072d60b6f8ce5d7 S: bc09734e243b9b2e9fe2f64c55aaf72073c6918b 172.16.1.100:7381 replicates fada90b0520d5aa3305bc89651cc7bb5de9b2a16 Can I set the above configuration? (type 'yes' to accept): yes ###是否在nodes配置文件中保存更新配置 >>> 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 172.16.1.100:6379) M: cdb8b1fe29feb9a564fbfed6599aa61dda250eb1 172.16.1.100:6379 slots:0-5460 (5461 slots) master M: a188b59b30056f61c1cf55ff5072d60b6f8ce5d7 172.16.1.100:6380 slots:5461-10922 (5462 slots) master M: fada90b0520d5aa3305bc89651cc7bb5de9b2a16 172.16.1.100:6381 slots:10923-16383 (5461 slots) master M: 818fb192631cf8c34bc58f24d2538444abe7d995 172.16.1.100:7379 slots: (0 slots) master replicates cdb8b1fe29feb9a564fbfed6599aa61dda250eb1 M: 1cf6ed9f8a5049513fb631e78207c7f0b1ba6674 172.16.1.100:7380 slots: (0 slots) master replicates a188b59b30056f61c1cf55ff5072d60b6f8ce5d7 M: bc09734e243b9b2e9fe2f64c55aaf72073c6918b 172.16.1.100:7381 slots: (0 slots) master replicates fada90b0520d5aa3305bc89651cc7bb5de9b2a16 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.

    檢查一下cluster的狀態:redis-trib.rb check 127.0.0.1:6379

9  接入cluster,並存入一個key/value (name/guo)

 

redis-cli -c -h 127.0.0.1 -p 6379

    注意:redis-cli在接入集羣模式時,要使用-c參數  

        能夠看到我在6379接入cluster中,並存入可一個name/guodalu鍵值對,可是cluster卻把我保存的值定位到7380去了,而我使用6381接入cluster中後,再次獲取name值時,會被自動的定位到7380上。咱們來看看是我是怎麼被定位到7380的。

        1 先計算guodalu的CRC16值是0xA36B,轉化成10進制是41835

        2 41835對16384取模是9067,9067 slot是落在6380上的,而7380是6380的slave。

    下面咱們把7380中止,能夠看到再次檢查cluster的時候,並不會報錯。

    咱們在6381上接入cluster,獲取name

10  恢復7380,中止6380

        能夠看到這時候check cluster的時候,會提示一個節點已經故障,咱們這時候再嘗試獲取key

        能夠看到仍是能夠獲取到的。若是把6380和7380同時中止呢?

        若是cluster中的master和slave所有故障的話,則這部分數據仍是不可用的。

11    在cluster中新增一個redis master節點

        啓動redis 8379實例

 

./redis-server /opt/redis-cluster/conf/redis-8379.conf

        而後把該實例加入cluster中:第一個參數是新加入的redis節點,第二個參數是cluster中的任何一個已有的節點

 

./redis-trib.rb add-node 127.0.0.1:8379 127.0.0.1:6379

 

    這時候檢查一下cluster狀態

 

./redis-trib.rb check 127.0.0.1:6379

 

        能夠看到8379已經加入cluster中了,默認是以master加入cluster的,並且副本爲1,可是這個新的master卻沒有被cluster分配slots,因此它裏面是沒有數據的,當有slave服務器要升級爲master服務器時,它不能參與選舉。這時候咱們可使用從新劃分slots的特性給新加入的8379遷移其餘節點上的slots,這個後面操做。

12    給8379添加一個slave節點

 

./redis-server /opt/redis-cluster/conf/redis-8380.conf ./redis-trib.rb add-node --slave --master-id 63ec1e7d57b3e19c6e1fa07768673001cfa0d30f 127.0.0.1:8380 127.0.0.1:6379 #或者 ./redis-trib.rb add-node --slave 127.0.0.1:8380 127.0.0.1:6379

        第一個添加slvae的命令是直接指定把該節點做爲哪一個master的slave加入cluster,而第二個命令redis-trib 會添加該新節點做爲一個具備較少副本的隨機的主服務器的副本(在本環境中顯然是8379的副本了)。

    再次檢查cluster狀態

 

./redis-trib.rb check 127.0.0.1:6379

 

        能夠看到8379已經有一個slave了,可是它的slots仍是0,由於咱們一直沒有給它遷移slots。

13  刪除一個redis slave節點

    咱們先刪除8380 slave節點,第一個參數是cluster中的任意一個節點,第二個參數要刪除的slave的ID

 

./redis-trib.rb del-node 172.16.1.100:6379 12decfd0d975ef0dcd151d0ff232da7a2269a1c2

 

        這時候再次檢查cluster的狀態

 

./redis-trib.rb check 127.0.0.1:6379

 

        注意,紅框中的8379的副本又變爲0了。

14  刪除一個redis master節點

        刪除master節點和刪除slave節點操做同樣,不過爲了移除一個master節點,它必須是空的。若是master不是空的,你須要先將其數據重分片到其餘的master節點。另外一種移除master節點的方式,就是在其從服務器上執行一次手工故障轉移,當它變爲了新的master的slave之後將其移除。

        由於咱們還沒操做slots從新分配,就先不刪除8379了。

15    cluster從新分片

        通過測試發現cluster的從新分片並非在新的cluster內再次均勻的分配slots,而是須要是手動指定把哪一個節點的多少slots遷移到另外一個節點去.......

 

./redis-trib.rb reshard 127.0.0.1:6379

看看最終結果

 

        在通過我一番瞎遷移後,最終成爲這樣了。固然你也可使用以下命令進行嚴格的遷移:

 

./redis-trib.rb reshard <host>:<port> --from <node-id> --to <node-id> --slots --yes

16  刪除8379 master節點

        由於8379的slots都被我遷移到其餘節點去了,而刪除一個master節點須要該節點的數據爲空

 

./redis-trib.rb del-node 127.0.0.1:6379 63ec1e7d57b3e19c6e1fa07768673001cfa0d30f

 

以上redis-trib的這些操做,在redis-cli中也是能夠進行操做的:

 

CLUSTER INFO 打印集羣的信息 CLUSTER NODES 列出集羣當前已知的全部節點(node),以及這些節點的相關信息。 CLUSTER RESET reset CLUSTER SAVECONFIG 強制節點保存集羣當前狀態到磁盤上。 CLUSTER SLOTS 得到slot在節點上的映射關係 CLUSTER MEET <ip> <port> 將 ip 和 port 所指定的節點添加到集羣當中,讓它成爲集羣的一份子。 CLUSTER FORGET <node_id> 從集羣中移除 node_id 指定的節點。 CLUSTER REPLICATE <node_id> 將當前節點設置爲 node_id 指定的節點的從節點。 CLUSTER SLAVES <node_id> 列出該slave節點的master節點 CLUSTER ADDSLOTS <slot> [slot ...] 將一個或多個槽(slot)指派(assign)給當前節點。 CLUSTER DELSLOTS <slot> [slot ...] 移除一個或多個槽對當前節點的指派。 CLUSTER FLUSHSLOTS 移除指派給當前節點的全部槽,讓當前節點變成一個沒有指派任何槽的節點。 CLUSTER SETSLOT <slot> NODE <node_id> 將槽 slot 指派給 node_id 指定的節點,若是槽已經指派給另外一個節點,那麼先讓另外一個節點刪除該槽>,而後再進行指派。 CLUSTER SETSLOT <slot> MIGRATING <node_id> 將本節點的槽 slot 遷移到 node_id 指定的節點中。 CLUSTER SETSLOT <slot> IMPORTING <node_id> 從 node_id 指定的節點中導入槽 slot 到本節點。 CLUSTER SETSLOT <slot> STABLE 取消對槽 slot 的導入(import)或者遷移(migrate)。 CLUSTER KEYSLOT <key> 計算鍵 key 應該被放置在哪一個槽上。 CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的鍵值對數量。 CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 個 slot 槽中的鍵 READONLY 在集羣中的salve節點開啓只讀模式 READWRITE 禁止讀取請求跳轉到集羣中的salve節點上
相關文章
相關標籤/搜索