1. 程式人生 > >Redis cluster叢集:原理及搭建

Redis cluster叢集:原理及搭建

1.為什麼使用redis?

redis是一種典型的no-sql 即非關係資料庫 像python的字典一樣 儲存key-value鍵值對 工作在memory中

所以很適合用來充當整個網際網路架構中各級之間的cache 比如lvs的4層轉發層 nginx的7層代理層

尤其是lnmp架構應用層如php-fpm或者是Tomcat到mysql之間 做一個cache 以減輕db的壓力

因為有相當一部分的資料 只是簡單的key-value對應關係,而且在實際的業務中常常在短時間內迅速變動 

如果用關係資料庫mysql之類儲存 會大大增加對db的訪問 導致db的負擔很重 因為所有的require中的大部分最後都要匯聚到db

所以如果想要業務穩定 那麼解決db的壓力 就是關鍵 所以現在大部分的解決方案就是在db層之上的各級使用多級的no-sql 

像memcache redis 等 來為db提供緩衝  

2.為什麼使用redis-cluster?

為了在大流量訪問下提供穩定的業務,叢集化是儲存的必然形態

未來的發展趨勢肯定是雲端計算和大資料的緊密結合 只有分散式架構能滿足要求

如果沒有叢集化 何來的分散式?

3.順帶一提總結一波今天的redis原理之資料持久化

雖然redis這種no-sql一般都是作為cache來服務  但是如果完全沒有資料可持久化的方法 那麼顯得有些單薄

就像memcache 由於這種no-sql是工作在memory的 那麼由於memory的實體是ram

所以如果重啟或者宕機 memory中的資料就全沒了 資料的一致性的不到保障 

但是 redis不同 redis有相對的資料持久化的方案 由兩種方式構成 aof & rdb

aof就像關係資料庫中的binlog一樣  把每一次寫操作以追加的形式記錄在其中以檔案的形式刷到磁盤裡 

並且可以使用不同的fsync策略     無fsync,每秒fsync,每次寫的時候fsync.

使用預設的每秒fsync策略,Redis的效能依然很好(fsync是由後臺執行緒進行處理的,主執行緒會盡力處理客戶端請求)

一旦出現故障,最多丟失1秒的資料.

但是缺點也隨之而來 那就是aof檔案的大小會隨著時間線性增長 一段時間之後 就會變得很大 

如果要在一端以AOF的形式來恢復資料 那麼由於AOF檔案的巨大體積 可能會讓程序如同假死一樣 十分的慢

rdb則是一種快照機制 

redis工作在記憶體中 rdb就是每隔一段時間 對記憶體中的資料做一次快照  儲存在rdb檔案中 

而且redis的主從同步可以實現非同步 也是由於rdb的機制 他在做快照時會fork出一個子程序 由子程序來做快照

父程序完全處理請求 毫不影響 很適合資料的備份 

但是問題是 如果資料量很大的話 rdb它要儲存一個完整的資料集 是一個大的工作 如果時間間隔設定的太短 

那麼嚴重影響redis的效能 但是按照常規設定的話 如5分鐘一次 那麼如果宕機或者重啟 就會基於上次做rdb的時間

從而丟失分鐘級的資料

point:在redis4.0的新特性中 採用了aof-rdb的混合方案來保障資料的永續性 但是官方的說法是還不成熟

是一個長期的工作  所以有待觀察吧 

4.redis叢集實現方案: 

關於redis的叢集化方案 目前有三種 

(1)Twitter開發的twemproxy

(2)豌豆莢開發的codis

(3)redis官方的redis-cluster

簡介:twemproxy架構簡單 就是用proxy對後端redis server進行代理 但是由於代理層的消耗效能很低 而且通常涉及多個key的操作都是不支援的 而且本身不支援動態擴容和透明的資料遷移 而且也失去維護 Twitter內部已經不使用了

          redis-cluster是三個裡效能最強大的 因為他使用去中心化的思想 使用hash slot方式 將16348個hash slot 覆蓋到所有節點上 對於儲存的每個key值 使用CRC16(KEY)&16348=slot 得到他對應的hash slot 並在訪問key時就去找他的hash slot在哪一個節點上 然後由當前訪問節點從實際被分配了這個hash slot的節點去取資料 節點之間使用輕量協議通訊 減少頻寬佔用 效能很高 自動實現負載均衡與高可用 自動實現failover  並且支援動態擴充套件 官方已經玩到可以1000個節點 實現的複雜度低 總之個人比較喜歡這個架構 因為他的去中心化思想免去了proxy的消耗 是全新的思路

           但是它也有一些不足 例如官方沒有提供圖形化管理工具 運維體驗差 全手工資料遷移 並且自己對自己本身的redis命令支援也不完全等 但是這些問題 我覺得不能掩蓋他關鍵的新思想所帶來的的優勢 隨著官方的推進 這些問題應該都能在一定時間內得到解決 那麼這時候去中心化思想帶來的高效能就會表現出他巨大的優勢 

          codis使用的也是proxy思路 但是做的比較好 是這兩種之間的一箇中間級 而且支援redis命令是最多的 有圖形化GUI管理和監控工具 運維友好 這個過段時間會詳細另外寫出來原理 工作機制和搭建實現

Redis叢集

基本介紹

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

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

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

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

  • 將資料自動切分split到多個節點的能力。
  • 當叢集中的一部分節點失效或者無法進行通訊時, 仍然可以繼續處理命令請求的能力。

叢集原理

redis-cluster架構圖

redis-cluster架構圖

  1. 所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位制協議優化傳輸速度和頻寬。

  2. 節點的fail是通過叢集中超過半數的節點檢測失效時才生效。

  3. 客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連線叢集所有節點,連線叢集中任何一個可用節點即可。

  4. redis-cluster把所有的物理節點對映到[0-16383]slot上,cluster 負責維護node<->slot<->value

Redis叢集中內建了 16384 個雜湊槽,當需要在 Redis 叢集中放置一個 key-value 時,redis 先對key 使用 crc16 演算法算出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的雜湊槽,redis 會根據節點數量大致均等的將雜湊槽對映到不同的節點

redis-cluster投票:容錯

redis-cluster投票:容錯

  1. 投票過程是叢集中所有master參與,如果半數以上master節點與master節點通訊超時(cluster-node-timeout),認為當前master節點掛掉.

  2. 什麼時候整個叢集不可用(cluster_state:fail)?

    • 如果叢集任意master掛掉,且當前master沒有slave.叢集進入fail狀態,也可以理解成叢集的slot對映[0-16383]不完整時進入fail狀態. 
      • redis-3.0.0.rc1加入cluster-require-full-coverage引數,預設關閉,開啟叢集相容部分失敗.
    • 如果叢集超過半數以上master掛掉,無論是否有slave,叢集進入fail狀態.

Redis叢集搭建

Redis單機版的安裝見部落格《redis入門——安裝篇》,安裝好之後,將redis複製成6份,注意要將.rdb.aof字尾的檔案刪除,如果有的話。

Ruby環境

使用

yum -y install ruby
yum -y install rubygems

安裝ruby環境。 
網上下載redis-3.0.0.gem,執行gem install redis-3.0.0.gem安裝。

redis配置檔案修改

現在已經準備好了,6份乾淨的redis,如下所示

[root@localhost redis-cluster]# pwd
/usr/local/redis/redis-cluster
[root@localhost redis-cluster]# ll
total 72
drwxr-xr-x 2 root root  4096 Nov  2 00:17 redis1
drwxr-xr-x 2 root root  4096 Nov  2 00:25 redis2
drwxr-xr-x 2 root root  4096 Nov  2 00:25 redis3
drwxr-xr-x 2 root root  4096 Nov  2 00:25 redis4
drwxr-xr-x 2 root root  4096 Nov  2 00:25 redis5
drwxr-xr-x 2 root root  4096 Nov  2 00:25 redis6
-rwxr-xr-x 1 root root 48141 Nov  2 00:16 redis-trib.rb
[root@localhost redis-cluster]# 

PS:注意,這裡已經將redis原始檔src目錄下的redis-trib.rb檔案拷貝過來了。 
redis-trib.rb這個檔案是redis叢集的管理檔案,ruby指令碼。

將六個節點的redis.conf配置檔案按照如下進行修改

################################ GENERAL  #####################################

# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes

# Accept connections on the specified port, default is 6379.
# If port 0 is specified Redis will not listen on a TCP socket.
port *

################################ REDIS CLUSTER  ###############################
#
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however
# in order to mark it as "mature" we need to wait for a non trivial percentage
# of users to deploy it in production.
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# Normal Redis instances can't be part of a Redis Cluster; only nodes that are
# started as cluster nodes can. In order to start a Redis instance as a
# cluster node enable the cluster support uncommenting the following:
#
cluster-enabled yes

PS:埠號如果是同一臺主機的話,必須不同。不同主機可以相同。

PS:我這裡是使用一臺主機,所以我將六個節點的埠號修改為7001-7006

編寫叢集啟動指令碼和停止指令碼

啟動指令碼start-all.sh
cd redis1
./redis-server redis.conf
cd ..
cd redis2
./redis-server redis.conf
cd ..
cd redis3
./redis-server redis.conf
cd ..
cd redis4
./redis-server redis.conf
cd ..
cd redis5
./redis-server redis.conf
cd ..
cd redis6
./redis-server redis.conf
cd ..
停止指令碼stop-all.sh
./redis1/redis-cli -p 7001 shutdown
./redis1/redis-cli -p 7002 shutdown
./redis1/redis-cli -p 7003 shutdown
./redis1/redis-cli -p 7004 shutdown
./redis1/redis-cli -p 7005 shutdown
./redis1/redis-cli -p 7006 shutdown

PS:兩個指令碼都放在如下所屬目錄

[root@localhost redis-cluster]# pwd
/usr/local/redis/redis-cluster
[root@localhost redis-cluster]# ll
total 80
drwxr-xr-x 2 root root  4096 Nov  2 00:52 redis1
drwxr-xr-x 2 root root  4096 Nov  2 00:51 redis2
drwxr-xr-x 2 root root  4096 Nov  2 00:53 redis3
drwxr-xr-x 2 root root  4096 Nov  2 00:53 redis4
drwxr-xr-x 2 root root  4096 Nov  2 00:53 redis5
drwxr-xr-x 2 root root  4096 Nov  2 00:53 redis6
-rwxr-xr-x 1 root root 48141 Nov  2 00:16 redis-trib.rb
-rw-r--r-- 1 root root   252 Nov  2 00:55 start-all.sh
-rw-r--r-- 1 root root   216 Nov  2 00:57 stop-all.sh
[root@localhost redis-cluster]# 
修改許可權
[[email protected] redis-cluster]# chmod -u+x start-all.sh stop-all.sh 

啟動節點

[root@localhost redis-cluster]# ./start-all.sh 
[root@localhost redis-cluster]# ps aux | grep redis
root      2924  0.8  0.1  33932  2048 ?        Ssl  Nov01   3:53 ./redis-server *:6379 [cluster]
root     11924  0.0  0.1  33936  1948 ?        Ssl  01:01   0:00 ./redis-server *:7001 [cluster]
root     11928  0.0  0.1  33936  1952 ?        Ssl  01:01   0:00 ./redis-server *:7002 [cluster]
root     11932  0.0  0.1  33936  1948 ?        Ssl  01:01   0:00 ./redis-server *:7003 [cluster]
root     11936  0.0  0.1  33936  1952 ?        Ssl  01:01   0:00 ./redis-server *:7004 [cluster]
root     11940  0.0  0.1  33936  1952 ?        Ssl  01:01   0:00 ./redis-server *:7005 [cluster]
root     11944  0.0  0.1  33936  1948 ?        Ssl  01:01   0:00 ./redis-server *:7006 [cluster]
root     11948  0.0  0.0   4360   748 pts/2    S+   01:01   0:00 grep redis
[root@localhost redis-cluster]# 

執行建立叢集命令

[root@localhost redis-cluster]# pwd
/usr/local/redis/redis-cluster
[root@localhost redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.37.131:7001 192.168.37.131:7002 192.168.37.131:7003 192.168.37.131:7004 192.168.37.131:7005  192.168.37.131:7006
成功啟動資訊
>>> Creating cluster
Connecting to node 192.168.37.131:7001: OK
Connecting to node 192.168.37.131:7002: OK
Connecting to node 192.168.37.131:7003: OK
Connecting to node 192.168.37.131:7004: OK
Connecting to node 192.168.37.131:7005: OK
Connecting to node 192.168.37.131:7006: OK
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.37.131:7001
192.168.37.131:7002
192.168.37.131:7003
Adding replica 192.168.37.131:7004 to 192.168.37.131:7001
Adding replica 192.168.37.131:7005 to 192.168.37.131:7002
Adding replica 192.168.37.131:7006 to 192.168.37.131:7003
M: 8b153503b52f83634e04b0077f32ef629ad91ee6 192.168.37.131:7001
   slots:0-5460 (5461 slots) master
M: f89799066dd8ecaaa1430559be4ce9c8c87055d8 192.168.37.131:7002
   slots:5461-10922 (5462 slots) master
M: 53d698ad56b09f89cfef34850213e2d0a44154dd 192.168.37.131:7003
   slots:10923-16383 (5461 slots) master
S: e73204399d08c14def1f71d0c5377cbc757dc4b8 192.168.37.131:7004
   replicates 8b153503b52f83634e04b0077f32ef629ad91ee6
S: 1d5dcc8d1ccb6bce55efc3e3aadc690dc77808d8 192.168.37.131:7005
   replicates f89799066dd8ecaaa1430559be4ce9c8c87055d8
S: e9458233cb85bd897ff694003e6d8a834eba2b44 192.168.37.131:7006
   replicates 53d698ad56b09f89cfef34850213e2d0a44154dd
Can I set the above configuration? (type 'yes' to accept): y
*** Aborting...
[[email protected] redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.37.131:7001 192.168.37.131:7002 192.168.37.131:7003 192.168.37.131:7004 192.168.37.131:7005  192.168.37.131:7006
>>> Creating cluster
Connecting to node 192.168.37.131:7001: OK
Connecting to node 192.168.37.131:7002: OK
Connecting to node 192.168.37.131:7003: OK
Connecting to node 192.168.37.131:7004: OK
Connecting to node 192.168.37.131:7005: OK
Connecting to node 192.168.37.131:7006: OK
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.37.131:7001
192.168.37.131:7002
192.168.37.131:7003
Adding replica 192.168.37.131:7004 to 192.168.37.131:7001
Adding replica 192.168.37.131:7005 to 192.168.37.131:7002
Adding replica 192.168.37.131:7006 to 192.168.37.131:7003
M: 8b153503b52f83634e04b0077f32ef629ad91ee6 192.168.37.131:7001
   slots:0-5460 (5461 slots) master
M: f89799066dd8ecaaa1430559be4ce9c8c87055d8 192.168.37.131:7002
   slots:5461-10922 (5462 slots) master
M: 53d698ad56b09f89cfef34850213e2d0a44154dd 192.168.37.131:7003
   slots:10923-16383 (5461 slots) master
S: e73204399d08c14def1f71d0c5377cbc757dc4b8 192.168.37.131:7004
   replicates 8b153503b52f83634e04b0077f32ef629ad91ee6
S: 1d5dcc8d1ccb6bce55efc3e3aadc690dc77808d8 192.168.37.131:7005
   replicates f89799066dd8ecaaa1430559be4ce9c8c87055d8
S: e9458233cb85bd897ff694003e6d8a834eba2b44 192.168.37.131:7006
   replicates 53d698ad56b09f89cfef34850213e2d0a44154dd
Can I set the above configuration? (type 'yes' to accept): yes
>>> 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.37.131:7001)
M: 8b153503b52f83634e04b0077f32ef629ad91ee6 192.168.37.131:7001
   slots:0-5460 (5461 slots) master
M: f89799066dd8ecaaa1430559be4ce9c8c87055d8 192.168.37.131:7002
   slots:5461-10922 (5462 slots) master
M: 53d698ad56b09f89cfef34850213e2d0a44154dd 192.168.37.131:7003
   slots:10923-16383 (5461 slots) master
M: e73204399d08c14def1f71d0c5377cbc757dc4b8 192.168.37.131: