1. 程式人生 > 實用技巧 >Redis操作及叢集搭建以及高可用配置

Redis操作及叢集搭建以及高可用配置

NoSQL - Redis 快取技術

Redis功能介紹

資料型別豐富     
支援持久化      
多種記憶體分配及回收策略
支援弱事務			
支援高可用                     
支援分散式分片叢集 

企業快取產品介紹

Memcached:
優點:高效能讀寫、單一資料型別、支援客戶端式分散式叢集、一致性hash
多核結構、多執行緒讀寫效能高。
缺點:無持久化、節點故障可能出現快取穿透、分散式需要客戶端實現、跨機房資料同步困難、架構擴容複雜度高
Redis:	優點:高效能讀寫、多資料型別支援、資料持久化、高可用架構、支援自定義虛擬記憶體、支援分散式分片叢集、單執行緒讀寫效能極高
缺點:多執行緒讀寫較Memcached慢
新浪、京東、直播類平臺、網頁遊戲

memcache  與redis在讀寫效能的對比
memcached 適合,多使用者訪問,每個使用者少量的rw
redis     適合,少使用者訪問,每個使用者大量rw	
			
Tair:
優點:高效能讀寫、支援三種儲存引擎(ddb、rdb、ldb)、支援高可用、支援分散式分片叢集、支撐了幾乎所有淘寶業務的快取。
	缺點:單機情況下,讀寫效能較其他兩種產品較慢

Redis使用場景介紹

Memcached:多核的快取服務,更加適合於多使用者併發訪問次數較少的應用場景
Redis:單核的快取服務,單節點情況下,更加適合於少量使用者,多次訪問的應用場景。
Redis一般是單機多例項架構,配合redis叢集出現。

Redis安裝部署:

下載:
wget http://download.redis.io/releases/redis-3.2.12.tar.gz
解壓:
上傳至 /data
tar xzf redis-3.2.12.tar.gz
mv redis-3.2.12 redis

安裝:
yum -y install gcc automake autoconf libtool make
cd redis
make

環境變數:
vim /etc/profile 
export PATH=/data/redis/src:$PATH
source /etc/profile 

啟動:
redis-server & 

連線測試:
redis-cli 
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> get num
10

Redis基本管理操作

基礎配置檔案介紹

mkdir /data/6379
cat > /data/6379/redis.conf<<EOF
daemonize yes
port 6379
logfile /data/6379/redis.log
dir /data/6379
dbfilename dump.rdb
EOF

redis-cli shutdown 
redis-server /data/6379/redis.conf 
netstat -lnp|grep 63

+++++++++++配置檔案說明++++++++++++++
redis.conf
是否後臺執行:
daemonize yes
預設埠:
port 6379
日誌檔案位置
logfile /var/log/redis.log
持久化檔案儲存位置
dir /data/6379
RDB持久化資料檔案:
dbfilename dump.rdb
++++++++++++++++++++++++++++++++++++++
redis-cli
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> get name
"zhangsan"

redis安全配置

redis預設開啟了保護模式,只允許本地迴環地址登入並訪問資料庫。
禁止protected-mode
protected-mode yes/no (保護模式,是否只允許本地訪問)

(1)Bind :指定IP進行監聽
vim /data/6379/redis.conf
bind 10.0.0.51  127.0.0.1

(2)增加requirepass  {password}
vim /data/6379/redis.conf
requirepass 123456


----------驗證-----
方法一:
[root@db03 ~]# redis-cli -a 123456
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> exit
方法二:
[root@db03 ~]# redis-cli
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> set a b
[root@db01 src]# redis-cli -a 123 -h 10.0.0.51 -p 6379
10.0.0.51:6379> set b 2
OK

線上檢視和修改配置

CONFIG GET *
CONFIG GET requirepass
CONFIG GET r*
CONFIG SET requirepass 123

redis持久化(記憶體資料儲存到磁碟)

RDB、AOF

RDB 持久化
	可以在指定的時間間隔內生成資料集的 時間點快照(point-in-time snapshot)。
    優點:速度快,適合於用做備份,主從複製也是基於RDB持久化功能實現的。
    缺點:會有資料丟失
rdb持久化核心配置引數:
vim /data/6379/redis.conf
dir /data/6379
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000

配置分別表示:
900秒(15分鐘)內有1個更改
300秒(5分鐘)內有10個更改
60秒內有10000個更改
  
AOF 持久化(append-only log file)
	記錄伺服器執行的所有寫操作命令,並在伺服器啟動時,通過重新執行這些命令來還原資料集。 
    AOF 檔案中的命令全部以 Redis 協議的格式來儲存,新命令會被追加到檔案的末尾。
    優點:可以最大程度保證資料不丟
    缺點:日誌記錄量級比較大

AOF持久化配置
appendonly yes
appendfsync always

appendfsync everysec
appendfsync no

是否開啟aof日誌功能
每1個命令,都立即同步到aof 
每秒寫1次
寫入工作交給作業系統,由作業系統判斷緩衝區大小,統一寫入到aof.

vim /data/6379/redis.conf
appendonly yes
appendfsync everysec 

面試: 
redis 持久化方式有哪些?有什麼區別?
rdb:基於快照的持久化,速度更快,一般用作備份,主從複製也是依賴於rdb持久化功能
aof:以追加的方式記錄redis操作日誌的檔案。可以最大程度的保證redis資料安全,類似於mysql的binlog


Redis資料型別:

## 6.1 介紹
String :      字元型別
Hash:         字典型別
List:         列表     
Set:          集合 
Sorted set:   有序集合

KEY的通用操作

KEYS * 	 keys a  keys a*	檢視已存在所有鍵的名字   ****
TYPE						返回鍵所儲存值的型別     ****
EXPIRE\ PEXPIRE 			以秒\毫秒設定生存時間    ***
TTL\ PTTL 					以秒\毫秒為單位返回生存時間 ***
PERSIST 					取消生存時間設定            ***
DEL							刪除一個key
EXISTS 				        檢查是否存在
RENAME 				        變更KEY名

---例子:
127.0.0.1:6379> set name zhangsan 
127.0.0.1:6379> EXPIRE name 60
(integer) 1
127.0.0.1:6379> ttl name
(integer) 57
127.0.0.1:6379> set a b ex 60
OK
127.0.0.1:6379> ttl a
127.0.0.1:6379> PERSIST a
(integer) 1
127.0.0.1:6379> ttl a
(integer) -1

Strings

應用場景
session 共享
常規計數:微博數,粉絲數,訂閱、禮物
key:value
(1)
 set name zhangsan 	
(2)
 MSET id 101 name zhangsan age 20 gender m
 等價於以下操作:
 SET id 101 
 set name zhangsan 
 set age 20 
 set gender m
(3)計數器
每點一次關注,都執行以下命令一次
127.0.0.1:6379> incr num
顯示粉絲數量:
127.0.0.1:6379> get num

暗箱操作:
127.0.0.1:6379> INCRBY num 10000
(integer) 10006
127.0.0.1:6379> get num
"10006"
127.0.0.1:6379> DECRBY num 10000
(integer) 6
127.0.0.1:6379> get num
"6"

hash型別(字典型別)

應用場景:
儲存部分變更的資料,如使用者資訊等。
最接近mysql表結構的一種型別
主要是可以做資料庫快取。

存資料:
hmset stu  id 101 name zhangsan age 20 gender m
hmset stu1 id 102 name zhangsan1 age 21 gender f

取資料:
HMGET stu id name age gender
HMGET stu1 id name age gender

select concat("hmset city_",id," id ",id," name ",name) from world.city limit 10 into outfile '/tmp/hmset.txt'

LIST(列表)

應用場景
訊息佇列系統
比如sina微博
在Redis中我們的最新微博ID使用了常駐快取,這是一直更新的。
但是做了限制不能超過5000個ID,因此獲取ID的函式會一直詢問Redis。
只有在start/count引數超出了這個範圍的時候,才需要去訪問資料庫。
系統不會像傳統方式那樣“重新整理”快取,Redis例項中的資訊永遠是一致的。
SQL資料庫(或是硬碟上的其他型別資料庫)只是在使用者需要獲取“很遠”的資料時才會被觸發,
而主頁或第一個評論頁是不會麻煩到硬碟上的資料庫了。

微信朋友圈:
 LPUSH wechat "today is 1 !"
 LPUSH wechat "today is 2 !"
 LPUSH wechat "today is 3 !"
 LPUSH wechat "today is 4 !"
 LPUSH wechat "today is 5 !"

[5,4,3,2,1]
 0 1 2 3 4 

[e,d,c,b,a]
0 1 2 3  4

127.0.0.1:6379> lrange wechat  0 0
1) "today is friday !"
127.0.0.1:6379> lrange wechat  0 1
1) "today is friday !"
2) "today is rainy  day !"
127.0.0.1:6379> lrange wechat  0 2
1) "today is friday !"
2) "today is rainy  day !"
3) "today is good  day !"
127.0.0.1:6379> lrange wechat  0 3
127.0.0.1:6379> lrange wechat  -2 -1
1) "today is bad day !"
2) "today is nice day !"

SET 集合型別(join union)

應用場景:
案例:在微博應用中,可以將一個使用者所有的關注人存在一個集合中,將其所有粉絲存在一個集合。
Redis還為集合提供了求交集、並集、差集等操作,可以非常方便的實現如共同關注、共同喜好、二度好友等功能,
對上面的所有集合操作,你還可以使用不同的命令選擇將結果返回給客戶端還是存集到一個新的集合中。

127.0.0.1:6379> sadd lxl pg1 jnl baoqiang gsy alexsb
(integer) 5
127.0.0.1:6379> sadd jnl baoqiang ms bbh yf wxg
(integer) 5
....

127.0.0.1:6379> SUNION lxl jnl
1) "baoqiang"
2) "yf"
3) "bbh"
4) "ms"
5) "wxg"
127.0.0.1:6379> SUNION lxl  jnl
1) "gsy"
2) "yf"
3) "alexsb"
4) "bbh"
5) "jnl"
6) "pg1"
7) "baoqiang"
8) "ms"
9) "wxg"
127.0.0.1:6379> 
	
......

127.0.0.1:6379> SINTER lxl jnl
1) "baoqiang"

........

127.0.0.1:6379> SDIFF jnl lxl
1) "wxg"
2) "yf"
3) "bbh"
4) "ms"
127.0.0.1:6379> 
127.0.0.1:6379> SDIFF lxl jnl
1) "jnl"
2) "pg1"
3) "gsy"
4) "alexsb"
.....

SortedSet(有序集合)

應用場景:
排行榜應用,取TOP N操作
這個需求與上面需求的不同之處在於,前面操作以時間為權重,這個是以某個條件為權重,比如按頂的次數排序,
這時候就需要我們的sorted set出馬了,將你要排序的值設定成sorted set的score,將具體的資料設定成相應的value,每次只需要執行一條ZADD命令即可。

127.0.0.1:6379> zadd topN 0 smlt 0 fskl 0 fshkl 0 lzlsfs 0 wdhbx 0 wxg 
(integer) 6
127.0.0.1:6379> ZINCRBY topN 100000 smlt
"100000"
127.0.0.1:6379> ZINCRBY topN 10000 fskl
"10000"
127.0.0.1:6379> ZINCRBY topN 1000000 fshkl
"1000000"
127.0.0.1:6379> ZINCRBY topN 100 lzlsfs
"100"
127.0.0.1:6379> ZINCRBY topN 10 wdhbx
"10"
127.0.0.1:6379> ZINCRBY topN 100000000 wxg
"100000000"

127.0.0.1:6379> ZREVRANGE topN 0 2 
1) "wxg"
2) "fshkl"
3) "smlt"
127.0.0.1:6379> ZREVRANGE topN 0 2 withscores
1) "wxg"
2) "100000000"
3) "fshkl"
4) "1000000"
5) "smlt"
6) "100000"
127.0.0.1:6379> 

Redis事務

redis的事務是基於佇列實現的。
mysql的事務是基於事務日誌和鎖機制實現的。
redis是樂觀鎖機制。

開啟事務功能時(multi)
multi 
command1      
command2
command3
command4
exec 
discard

4條語句作為一個組,並沒有真正執行,而是被放入同一佇列中。
如果,這時執行discard,會直接丟棄佇列中所有的命令,而不是做回滾。
exec
當執行exec時,對列中所有操作,要麼全成功要麼全失敗

127.0.0.1:6379> set a b
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a b
QUEUED
127.0.0.1:6379> set c d
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK

redis(Master-Replicaset) *****

原理:

1. 副本庫通過slaveof 10.0.0.51 6379命令,連線主庫,併發送SYNC給主庫 
2. 主庫收到SYNC,會立即觸發BGSAVE,後臺儲存RDB,傳送給副本庫
3. 副本庫接收後會應用RDB快照
4. 主庫會陸續將中間產生的新的操作,儲存併發送給副本庫
5. 到此,我們主複製集就正常工作了
6. 再此以後,主庫只要發生新的操作,都會以命令傳播的形式自動傳送給副本庫.
7. 所有複製相關資訊,從info資訊中都可以查到.即使重啟任何節點,他的主從關係依然都在.
8. 如果發生主從關係斷開時,從庫資料沒有任何損壞,在下次重連之後,從庫傳送PSYNC給主庫
9. 主庫只會將從庫缺失部分的資料同步給從庫應用,達到快速恢復主從的目的

主從資料一致性保證

min-slaves-to-write 1
min-slaves-max-lag  3

主庫是否要開啟持久化?

如果不開有可能,主庫重啟操作,造成所有主從資料丟失!

主從複製實現

1、環境:
準備兩個或兩個以上redis例項

mkdir /data/638{0..2}

配置檔案示例:
cat >> /data/6380/redis.conf <<EOF
port 6380
daemonize yes
pidfile /data/6380/redis.pid
loglevel notice
logfile "/data/6380/redis.log"
dbfilename dump.rdb
dir /data/6380
requirepass 123
masterauth 123
EOF


cat >>   /data/6381/redis.conf <<EOF
port 6381
daemonize yes
pidfile /data/6381/redis.pid
loglevel notice
logfile "/data/6381/redis.log"
dbfilename dump.rdb
dir /data/6381
requirepass 123
masterauth 123
EOF


cat >>   /data/6382/redis.conf <<EOF
port 6382
daemonize yes
pidfile /data/6382/redis.pid
loglevel notice
logfile "/data/6382/redis.log"
dbfilename dump.rdb
dir /data/6382
requirepass 123
masterauth 123
EOF


啟動:
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf

主節點:6380
從節點:6381、6382

2、開啟主從:
6381/6382命令列:

redis-cli -p 6381 -a 123 SLAVEOF 127.0.0.1 6380
redis-cli -p 6382 -a 123 SLAVEOF 127.0.0.1 6380


3、查詢主從狀態
 redis-cli -p 6380 -a 123 info replication



redis-sentinel(哨兵)

1、監控
2、自動選主,切換(6381 slaveof no one)
   採用的是raft分散式一致性協議進行選主:資料節接近主,可以和大部分節點聯絡,少數服從多數。
3、重構主從管理
4、應用透明 
5、自動處理故障節點


sentinel搭建過程
mkdir /data/26380

cd /data/26380

vim sentinel.conf
port 26380
dir "/data/26380"
sentinel monitor mymaster 127.0.0.1 6380 1
sentinel down-after-milliseconds mymaster 5000
sentinel auth-pass mymaster 123 

啟動:
[root@db01 26380]# redis-sentinel /data/26380/sentinel.conf  &>/tmp/sentinel.log &

cluster叢集搭建]

以前要搞redis叢集,得藉助一致性hash來自己搞sharding,現在方便多了,直接上cluster功能就行了,而且還支援節點動態新增、HA、節點增減後快取重新分佈(resharding)。

下面是參考官方教程cluster-tutorial 在mac機上搭建cluster的過程:

一、下載最新版redis 編譯

目前最新版是3.0.7,下載地址:http://www.redis.io/download

編譯很簡單,一個make命令即可

二、建6個目錄

mkdir ~/app/redis-cluster/  #先建一個根目錄
mkdir 7000 7001 7002 7003 7004 7005

注:與大多數分散式中介軟體一樣,redis的cluster也是依賴選舉演算法來保證叢集的高可用,所以類似ZK一樣,一般是奇數個節點(可以允許N/2以下的節點失效),再考慮到每個節點做Master-Slave互為備份,所以一個redis cluster叢集最少也得6個節點。

然後把步驟1裡編譯好的redis,複製到這6個目錄下。

三、配置檔案

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

把上面這段儲存成redis-cluster.conf,放到每個目錄的redis目錄中,注意修改port埠,即7000目錄下的port為7000,7001目錄下的port為7001...

cluster-node-timeout 是叢集中各節點相互通訊時,允許"失聯"的最大毫秒數,上面的配置為5秒,如果超過5秒某個節點沒向其它節點彙報成功,認為該節點掛了。

四、依次啟動各個redis

在每個目錄redis的src子目錄下,輸入:

./redis-server ../redis-cluster.conf

這樣7000~7005這6個節點就啟動了。

五、安裝redis的ruby模組

brew update
brew install ruby
sudo gem install redis #注:這個步驟建議翻^牆

解釋:雖然步驟4把6個redis server啟動成功了,但是彼此之間是完全獨立的,需要藉助其它工具將其加入cluster,而這個工具就是redis提供的一個名為redis-trib.rb的ruby指令碼(個人估計redis的作者比較偏愛ruby),mac自帶了ruby2.0環境,但是沒有redis模組,所以要安裝這玩意兒,否則接下來的建立cluster將失敗。

六、建立cluster

./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

仍然保持在某個目錄的src子目錄下,執行上面這段shell指令碼,cluster就建立成功了,replicas 1的意思,就是每個節點建立1個副本(即:slave),所以最終的結果,就是後面的127.0.0.1:7000~127.0.0.1:7005中,會有3個會指定成master,而其它3個會指定成slave。

注:利用redis-trib建立cluster的操作,只需要一次即可,假設系統關機,把所有6個節點全關閉後,下次重啟後,即自動進入cluster模式,不用再次redis-trib.rb create。

此時,如何用ps檢視redis程序,會看到每個程序後附帶了cluster的字樣

如果想知道,哪些埠的節點是master,哪些埠的節點是slave,可以用下面的命令:

./redis-trib.rb check 127.0.0.1:7000

輸出結果如下:

>>> Performing Cluster Check (using node 127.0.0.1:7000)
S: 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e 127.0.0.1:7000
   slots: (0 slots) slave
   replicates 38910c5baafea02c5303505acfd9bd331c608cfc
M: e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa 127.0.0.1:7001
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: 88e16f91609c03277f2ee6ce5285932f58c221c1 127.0.0.1:7005
   slots: (0 slots) slave
   replicates ec964a7c7cd53b986f54318a190c1426fc53a5fa
S: be7e9fd3b7d096b037306bc14e1017150fa59d7a 127.0.0.1:7004
   slots: (0 slots) slave
   replicates e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa
M: 38910c5baafea02c5303505acfd9bd331c608cfc 127.0.0.1:7003
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: ec964a7c7cd53b986f54318a190c1426fc53a5fa 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.

從上面的輸出,可以看出7000、7004、7005是slave,而7001、7003、7002是master(如果大家人為做過一些failover的測試,比如把某個節點手動停掉,再恢復,輸出的結果可能與上面不太一樣),除了check引數,還有一個常用的引數info

./redis-trib.rb info 127.0.0.1:7000

輸出結果如下:

127.0.0.1:7001 (e0e8dfdd...) -> 2 keys | 5462 slots | 1 slaves.
127.0.0.1:7003 (38910c5b...) -> 2 keys | 5461 slots | 1 slaves.
127.0.0.1:7002 (ec964a7c...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 4 keys in 3 masters.
0.00 keys per slot on average.

它會把所有的master資訊輸出,包括這個master上有幾個快取key,有幾個slave,所有master上的keys合計,以及平均每個slot上有多少key,想了解更多redis-trib指令碼的其它引數,可以用

./redis-trib.rb help

輸出如下:

Usage: redis-trib <command> <options> <arguments ...>
 
  create          host1:port1 ... hostN:portN
                  --replicas <arg>
  check           host:port
  info            host:port
  fix             host:port
                  --timeout <arg>
  reshard         host:port
                  --from <arg>
                  --to <arg>
                  --slots <arg>
                  --yes
                  --timeout <arg>
                  --pipeline <arg>
  rebalance       host:port
                  --weight <arg>
                  --auto-weights
                  --use-empty-masters
                  --timeout <arg>
                  --simulate
                  --pipeline <arg>
                  --threshold <arg>
  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>
                  --copy
                  --replace
  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.

上面已經多次出現了slot這個詞,略為解釋一下:

如上圖,redis-cluster把整個叢集的儲存空間劃分為16384個slot(譯為:插槽?),當6個節點分為3主3從時,相當於整個cluster中有3組HA的節點,3個master會平均分攤所有slot,每次向cluster中的key做操作時(比如:讀取/寫入快取),redis會對key值做CRC32演算法處理,得到一個數值,然後再對16384取模,通過餘數判斷該快取項應該落在哪個slot上,確定了slot,也就確定了儲存在哪個master節點上,當cluster擴容或刪除節點時,只需要將slot重新分配即可(即:把部分slot從一些節點移動到其它節點)。

七、redis-cli客戶端操作

./redis-cli -c -h localhost -p 7000

注意加引數-c,表示進入cluster模式,隨便新增一個快取試試:

localhost:7000> set user1 jimmy
-> Redirected to slot [8106] located at 127.0.0.1:7001
OK

注意第2行的輸出,表示user1這個快取通過計算後,落在8106這個slot上,最終定位在7001這個埠對應的節點上(解釋:因為7000是slave,7001才是master,只有master才能寫入),如果是在7001上重複上面的操作時,不會出現第2行(解釋:7001是master,所以不存在redirect的過程)

➜  src ./redis-cli -c -h localhost -p 7001
localhost:7001> set user1 yang
OK
localhost:7001>

八、FailOver測試

先用redis-trib.rb 檢視下當前的主、從情況

➜  src ./redis-trib.rb check localhost:7000
>>> Performing Cluster Check (using node localhost:7000)
S: 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e localhost:7000
   slots: (0 slots) slave
   replicates 38910c5baafea02c5303505acfd9bd331c608cfc
M: ec964a7c7cd53b986f54318a190c1426fc53a5fa 127.0.0.1:7002
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
M: e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa 127.0.0.1:7001
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: be7e9fd3b7d096b037306bc14e1017150fa59d7a 127.0.0.1:7004
   slots: (0 slots) slave
   replicates e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa
S: 88e16f91609c03277f2ee6ce5285932f58c221c1 127.0.0.1:7005
   slots: (0 slots) slave
   replicates ec964a7c7cd53b986f54318a190c1426fc53a5fa
M: 38910c5baafea02c5303505acfd9bd331c608cfc 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(38910c5baafea02c5303505acfd9bd331c608cfc)的slave,現在我們人工把7003的redis程序給kill掉,然後觀察7000的終端輸出:

872:S 21 Mar 10:55:55.663 * Connecting to MASTER 127.0.0.1:7003
3872:S 21 Mar 10:55:55.663 * MASTER <-> SLAVE sync started
3872:S 21 Mar 10:55:55.663 # Error condition on socket for SYNC: Connection refused
3872:S 21 Mar 10:55:55.771 * Marking node 38910c5baafea02c5303505acfd9bd331c608cfc as failing (quorum reached).
3872:S 21 Mar 10:55:55.771 # Cluster state changed: fail
3872:S 21 Mar 10:55:55.869 # Start of election delayed for 954 milliseconds (rank #0, offset 183).
3872:S 21 Mar 10:55:56.703 * Connecting to MASTER 127.0.0.1:7003
3872:S 21 Mar 10:55:56.703 * MASTER <-> SLAVE sync started
3872:S 21 Mar 10:55:56.703 # Error condition on socket for SYNC: Connection refused
3872:S 21 Mar 10:55:56.909 # Starting a failover election for epoch 10.
3872:S 21 Mar 10:55:56.911 # Failover election won: I'm the new master.
3872:S 21 Mar 10:55:56.911 # configEpoch set to 10 after successful failover
3872:M 21 Mar 10:55:56.911 * Discarding previously cached master state.
3872:M 21 Mar 10:55:56.911 # Cluster state changed: ok

注意5,6,11這幾行,第5行表明由於7003宕機,cluster狀態已經切換到fail狀態,第6行表示發起選舉,第11行表示7000埠對應的節點當選為new master。

注:如果一組分片中的master、slave全掛了,整個cluster叢集不再接受任何讀/寫指令,redis-cli終端裡會直接報cluster down,但是info等其它指令仍然可用,直到這一組分片中,有一個節點恢復為止。

九、cluster 擴容

業務規模變大後,叢集擴容是早晚的事情,下面演示如何再新增2個節點,先把7000複製二份,變成7006,7007,然後進入7006/7007目錄redis的src子目錄下

rm nodes.conf dump.rdb appendonly.aof

由於7000我們剛才啟動過,裡面有已經有一些資料了,所以要把資料檔案,日誌檔案,以及cluster的nodes.conf檔案刪除,變成一個空的redis獨立節點,否則無法加入cluster。

然後修改redis-cluster.conf

port 7000
cluster-enabled yes
cluster-config-file "nodes.conf"
cluster-node-timeout 10000
appendonly yes
# Generated by CONFIG REWRITE
dir "/Users/yjmyzz/app/redis-cluster/7000/redis-3.0.7/src"

要修改的地方有二處,1是第一行的埠,改成與7006/7007匹配的埠,2是最後2行,這是7000執行後,自動新增的,把最後二行刪除。

做完這些後,啟動7006,7007這二個redis節點,此時這2個新節點與cluster沒有任何關係,可以用下面的命令將7006做為master新增到cluster中。

./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000

注:第1個引數為新節點的"IP:埠",第2個引數為叢集中的任一有效的節點。

順利的話,輸出如下:

>>> 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: 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e 127.0.0.1:7000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa 127.0.0.1:7001
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: be7e9fd3b7d096b037306bc14e1017150fa59d7a 127.0.0.1:7004
   slots: (0 slots) slave
   replicates e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa
M: ec964a7c7cd53b986f54318a190c1426fc53a5fa 127.0.0.1:7002
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 88e16f91609c03277f2ee6ce5285932f58c221c1 127.0.0.1:7005
   slots: (0 slots) slave
   replicates ec964a7c7cd53b986f54318a190c1426fc53a5fa
S: 38910c5baafea02c5303505acfd9bd331c608cfc 127.0.0.1:7003
   slots: (0 slots) slave
   replicates 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e
[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.

可以再用check確認下狀態:

➜  src ./redis-trib.rb check 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e 127.0.0.1:7000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa 127.0.0.1:7001
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: be7e9fd3b7d096b037306bc14e1017150fa59d7a 127.0.0.1:7004
   slots: (0 slots) slave
   replicates e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa
M: 226d1af3c95bf0798ea9fed86373b89347f889da 127.0.0.1:7006
   slots: (0 slots) master
   0 additional replica(s)
M: ec964a7c7cd53b986f54318a190c1426fc53a5fa 127.0.0.1:7002
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 88e16f91609c03277f2ee6ce5285932f58c221c1 127.0.0.1:7005
   slots: (0 slots) slave
   replicates ec964a7c7cd53b986f54318a190c1426fc53a5fa
S: 38910c5baafea02c5303505acfd9bd331c608cfc 127.0.0.1:7003
   slots: (0 slots) slave
   replicates 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

12-14行說明7006已經是cluster的新master了,繼續,用下面的命令把7007當成slave加入:

./redis-trib.rb add-node --slave --master-id 226d1af3c95bf0798ea9fed86373b89347f889da 127.0.0.1:7007 127.0.0.1:7000

這裡多出了二個引數:--slave 表示準備將新節點當成slave加入,--master-id xxxxx 則是指定要當誰的slave,後面的xxx部分,即為前面check的輸出結果中,7006的ID,完事之後,可以再次確認狀態:

➜  src ./redis-trib.rb check 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e 127.0.0.1:7000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: 792bcccf35845c4922dd33d7f9827420ebb89bc9 127.0.0.1:7007
   slots: (0 slots) slave
   replicates 226d1af3c95bf0798ea9fed86373b89347f889da
M: e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa 127.0.0.1:7001
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: be7e9fd3b7d096b037306bc14e1017150fa59d7a 127.0.0.1:7004
   slots: (0 slots) slave
   replicates e0e8dfddd4e9d855090d6efd18e55ea9c0e1f7aa
M: 226d1af3c95bf0798ea9fed86373b89347f889da 127.0.0.1:7006
   slots: (0 slots) master
   1 additional replica(s)
M: ec964a7c7cd53b986f54318a190c1426fc53a5fa 127.0.0.1:7002
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 88e16f91609c03277f2ee6ce5285932f58c221c1 127.0.0.1:7005
   slots: (0 slots) slave
   replicates ec964a7c7cd53b986f54318a190c1426fc53a5fa
S: 38910c5baafea02c5303505acfd9bd331c608cfc 127.0.0.1:7003
   slots: (0 slots) slave
   replicates 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

觀察6-8行、15-17行,說明7007已經是7006的slave。

十、reshard 重新劃分slot

增加新的節點之後,問題就來了,16384個slot已經被其它3組節點分完了,新節點沒有slot,沒辦法存放快取,所以需要將slot重新分佈。

➜  src ./redis-trib.rb info 127.0.0.1:7000
127.0.0.1:7000 (0b7e0d53...) -> 4 keys | 5461 slots | 1 slaves.
127.0.0.1:7001 (e0e8dfdd...) -> 4 keys | 5462 slots | 1 slaves.
127.0.0.1:7006 (226d1af3...) -> 0 keys | 0 slots | 1 slaves. #7006上完全沒有slot
127.0.0.1:7002 (ec964a7c...) -> 9 keys | 5461 slots | 1 slaves.
[OK] 17 keys in 4 masters.
0.00 keys per slot on average.

用下面的命令可以重新分配slot

./redis-trib.rb reshard 127.0.0.1:7000

reshard後面的IP:port,只要是在cluster中的有效節點即可。

➜  src ./redis-trib.rb reshard 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e 127.0.0.1:7000
   slots:1792-4095 (2304 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.
How many slots do you want to move (from 1 to 16384)? 1000 #這裡輸入要移動多少slot
What is the receiving node ID? 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e #這裡輸入目標節點的id
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:all #將所有node都當成源節點
    ...
    Moving slot 4309 from ec964a7c7cd53b986f54318a190c1426fc53a5fa
    Moving slot 4310 from ec964a7c7cd53b986f54318a190c1426fc53a5fa
    Moving slot 4311 from ec964a7c7cd53b986f54318a190c1426fc53a5fa
    Moving slot 4312 from ec964a7c7cd53b986f54318a190c1426fc53a5fa
    Moving slot 4313 from ec964a7c7cd53b986f54318a190c1426fc53a5fa
Do you want to proceed with the proposed reshard plan (yes/no)? yes #確認執行

注:第一個互動詢問,填寫多少slot移動時,要好好想想,如果填成16384,則將所有slot都移動到一個固定節點上,會導致更加不均衡!建議每次移動500~1000,這樣對線上的影響比較小。

另外在填寫source node時,除了all之外,還可以直接填寫源節點的id,即:

[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 300
What is the receiving node ID? 0b7e0d5337e87ac7b59bba4c1248e5c9e8d1905e
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:226d1af3c95bf0798ea9fed86373b89347f889da #這裡填寫源節點的id
Source node #2:done #這裡輸入done表示,不再繼續新增源節點了

reshard可以多次操作,直到達到期望的分佈為止(注:個人覺得redis的reshard這裡有點麻煩,要移動多少slot需要人工計算,如果能提供一個引數之類,讓16384個slot自動平均分配就好了),調整完成後,可以再看看分佈情況:

➜  src ./redis-trib.rb info 127.0.0.1:7000
127.0.0.1:7000 (0b7e0d53...) -> 4 keys | 4072 slots | 0 slaves.
127.0.0.1:7001 (e0e8dfdd...) -> 5 keys | 4099 slots | 0 slaves.
127.0.0.1:7006 (226d1af3...) -> 5 keys | 4132 slots | 4 slaves.
127.0.0.1:7002 (ec964a7c...) -> 3 keys | 4081 slots | 0 slaves.
[OK] 17 keys in 4 masters.
0.00 keys per slot on average.

十一、刪除節點del-node

既然有擴容,就會有反向需求,某些節點不再需要時,可以用del-node刪除,比如剛才我一陣亂倒騰後,發現7006已經有4個slave了,而其它master一個slave都沒有,這明顯不合理。

刪除節點命令:

./redis-trib.rb del-node 127.0.0.1:7006 88e16f91609c03277f2ee6ce5285932f58c221c1

del-node後面的ip:port只要是cluster中有效節點即可,最後一個引數為目標節點的id,注意:只有slave節點和空的master節點可以刪除,如果master非空,先用reshard把上面的slot移動到其它node後再刪除,如果有一組master-slave節點,將master上所有slot移到其它節點,然後將master刪除,剩下的slave會另尋他主,變成其它master的slave。

另外:刪除節點的含義,不僅僅是從cluster中將這個節點移除,還會直接將目標節點的redis服務停止。