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
節點彼此互聯(PING
-PONG
機制),內部使用二進位制協議優化傳輸速度和頻寬。節點的
fail
是通過叢集中超過半數的節點檢測失效時才生效。客戶端與
redis
節點直連,不需要中間proxy
層.客戶端不需要連線叢集所有節點,連線叢集中任何一個可用節點即可。redis-cluster
把所有的物理節點對映到[0-16383
]slot
上,cluster
負責維護node
<->slot
<->value
Redis
叢集中內建了 16384
個雜湊槽,當需要在 Redis
叢集中放置一個 key-value
時,redis
先對key
使用 crc16
演算法算出一個結果,然後把結果對 16384
求餘數,這樣每個 key
都會對應一個編號在 0-16383
之間的雜湊槽,redis
會根據節點數量大致均等的將雜湊槽對映到不同的節點
redis-cluster投票:容錯
投票過程是叢集中所有
master
參與,如果半數以上master
節點與master
節點通訊超時(cluster-node-timeout
),認為當前master
節點掛掉.什麼時候整個叢集不可用(
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: