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服務停止。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM