參考文檔:
- redis官網:https://redis.io/
- redis集群教程:http://www.redis.cn/topics/cluster-tutorial.html
- 單機安裝參考:http://www.runoob.com/redis/redis-install.html
- 集群部署參考:http://zyxjohn.blog.51cto.com/5313197/1932714
- 配置文件參考:http://www.cnblogs.com/zhoujinyi/p/6430116.html
- 集群管理,測試參考:http://www.cnblogs.com/zhoujinyi/p/6477133.html
本文簡單介紹redis及其集群的配置操作。
一.環境
1. OS
Server:CentOS-7-x86_64-1511
Node1:10.11.4.201
Node2:10.11.4.202
Node3:10.11.4.203
2. redis版本
redis-4.0.0:https://redis.io/download
rubygemredis-3.3.3.gem:https://rubygems.org/gems/redis
二.安裝redis服務器
以node1為例,node2/node3配置根據環境微調。
1. 安裝redis
[root@node1 ~]# cd /usr/local/src/ [root@node1 src]# wget http://download.redis.io/releases/redis-4.0.0.tar.gz [root@node1 src]# tar -zxvf redis-4.0.0.tar.gz [root@node1 src]# cd redis-4.0.0 #沒有“configure”,直接“make”; #在安裝的時候指定“PREFIX”,注意不帶“--”,不指定時默認安裝到“/usr/local/bin”下; #解壓目錄下的“src/“下的二進制文件可直接運行 [root@node1 redis-4.0.0]# make [root@node1 redis-4.0.0]# make PREFIX=/usr/local/redis install
2. 設置環境變量
#如果是單節點,至此就已安裝完成,可直接啟動; #啟動指令:”/usr/local/redis/redis-server“,采用默認配置; #也可在啟動指令后帶”redis.conf”文件指定conf文件; #”redis.conf”默認文件在解壓目錄下有范本,可直接使用 [root@node1 src]# cd /usr/local/redis/ [root@node1 redis]# ln -s /usr/local/redis/bin/* /usr/local/bin/
3. 配置redis.conf文件
#預設redis運行的pid,log文件,數據等的目錄等 [root@node1 ~]# cd /usr/local/redis/ [root@node1 redis]# mkdir -p /usr/local/redis/etc [root@node1 redis]# mkdir -pv /usr/local/redis/var/{run,log} [root@node1 redis]# mkdir -pv /usr/local/redis/var/lib/{redis_6379,redis_6380} #redis以進程為實例,可以根據每台宿主機的性能與redis的負載運行若干實例,只要區分每個實例的監聽端口即可; #本實驗每台宿主機運行兩個實例以驗證主從的自動分配; #以監聽端口重新命名redis.conf文件 [root@node1 redis]# cp /usr/local/src/redis-4.0.0/redis.conf /usr/local/redis/etc/redis_6379.conf #對於集群配置,以下配置文件中,配置深藍粗體字部分即可,其余可采用默認配置; #redis.conf文件的詳細配置請參考:http://www.cnblogs.com/zhoujinyi/p/6430116.html; #本宿主機的另一個實例conf文件請參考以上配置修改,可以復制已配置完成的redis_6379.conf為redis_6380.conf后,vim采用“:1,$s/6379/6380/gc”修改。 [root@node1 redis]# vim /usr/local/redis/etc/redis_6379.conf #監聽地址,這里設置全地址監控; #開啟/關閉實例的腳本中,關閉實例時調用的是127.0.0.1地址。 bind 0.0.0.0 #監聽端口 port 6379 # 默認沒有在后台執行 daemonize yes # 定義pid與log路徑,根據上文預設配置 pidfile /usr/local/redis/var/run/redis_6379.pid logfile "/usr/local/redis/var/log/redis_6379.log" # 集群模式默認是關閉的 cluster-enabled yes # 集群配置文件的名稱,每個節點都有一個集群相關的配置文件,持久化保存集群的信息; # 此文件不需要手動配置,由Redis生成並更新; # 每個Redis集群節點/實例需要一個單獨的配置文件,同一宿主機系統中不同實例的配置文件名稱不能沖突 cluster-config-file nodes-6379.conf # 集群節點超時時間,單位毫秒,集群內部多種通信時間限制以此超時時間倍數為准 cluster-node-timeout 15000 #由redis服務器自動生成,默認情況下,redis服務器程序會定期自動對數據庫做一次遍歷,把內存快照寫在一個叫做“dump.rdb”的文件里,這個持久化機制叫做SNAPSHOT; #有了SNAPSHOT后,如果服務器宕機,重新啟動redis服務器程序時,redis會自動加載dump.rdb,將數據庫狀態恢復到上一次做SNAPSHOT時的狀態 dbfilename "dump.rdb" #數據目錄,數據庫寫入此目錄,redis 存儲分為內存儲存、磁盤存儲和log文件三部分; #rdb、aof文件也寫在此目錄; #rdb持久化可以在指定的時間間隔內生成數據集的時間點快照(point-in-time snapshot);即全量數據(rdb):把內存中的數據寫入磁盤,便於下次讀取文件進行加載; #aof持久化可以記錄服務器執行的所有寫操作命令,並在服務器啟動時,通過重新執行這些命令來還原數據集;aof文件中的命令全部以redis協議的格式來保存,新命令會被追加到文件的末尾;redis 還可以在后台對aof文件進行重寫(rewrite),使得aof文件的體積不會超出保存數據集狀態所需的實際大小;即增量請求(aof):是把內存中的數據序列化為操作請求,用於讀取文件進行replay得到數據; #redis還可以同時使用aof持久化與rdb持久化,此時,當redis重啟時,它會優先使用aof 文件還原數據集,因為aof文件保存的數據集通常比rdb文件所保存的數據集更完整。 dir "/usr/local/redis/var/lib/redis_6379"
4. 配置開機啟動
1)配置啟動腳本
#配置啟動腳本,根據前文的配置文件修改相關參數,可對每個實例分別配置; #配置啟動腳本主要是方便啟動/關閉服務,不必每次指定redis.conf文件; #也可通過”redisl-server &”或者”redis-server xxx/redis.conf”等方式啟動服務; #以下啟動腳本的藍色粗體字體基於默認配置的修改或新增 [root@node1 ~]# cp /usr/local/src/redis-4.0.0/utils/redis_init_script /etc/init.d/redis_6379 [root@node1 ~]# vim /etc/init.d/redis_6379 #!/bin/sh # chkconfig: 35 10 90 #重要!否則chkconfig不能識別開機啟動服務項; #”35”為運行級別,”10”為啟動優先級,”90”為關閉優先級 # # Simple Redis init.d script conceived to work on Linux systems # as it does use of the /proc filesystem. REDISPORT=6379 # EXEC=/usr/local/bin/redis-server EXEC=/usr/local/redis/bin/redis-server # CLIEXEC=/usr/local/bin/redis-cli CLIEXEC=/usr/local/redis/bin/redis-cli # PIDFILE=/var/run/redis_${REDISPORT}.pid PIDFILE=/usr/local/redis/var/run/redis_${REDISPORT}.pid # CONF="/etc/redis/${REDISPORT}.conf" CONF="/usr/local/redis/etc/redis_${REDISPORT}.conf" USER=redis #添加執行用戶變量 case "$1" in start) if [ -f $PIDFILE ] then echo "$PIDFILE exists, process is already running or crashed" else echo "Starting Redis server..." # $EXEC $CONF su - $USER -c "$EXEC $CONF" #非root賬號啟動服務 fi ;; stop) if [ ! -f $PIDFILE ] then echo "$PIDFILE does not exist, process is not running" else PID=$(cat $PIDFILE) echo "Stopping ..." # $CLIEXEC -p $REDISPORT shutdown su - $USER -c "$CLIEXEC -p $REDISPORT shutdown" #非root賬號關閉服務 while [ -x /proc/${PID} ] do echo "Waiting for Redis to shutdown ..." sleep 1 done echo "Redis stopped" fi ;; restart|force-reload) ${0} stop ${0} start ;; *) echo "Usage: /etc/init.d/redis {start|stop|restart|force-reload}" >&2 exit 1 Esac [root@node1 ~]# cp /etc/init.d/redis_6379 /etc/init.d/redis_6380 [root@node1 ~]# vim /etc/init.d/redis_6380 #修改“REDISPORT”變量值 REDISPORT=6380
2)設置開機啟動
[root@node1 ~]# chkconfig --add redis_6379 [root@node1 ~]# chkconfig --level 35 redis_6379 on #默認runlevel 35已打開 [root@node1 ~]# chkconfig --add redis_6380 [root@node1 ~]# chkconfig --level 35 redis_6380 on
5. 創建賬號並賦權
#因為redis很容易對服務器進行root賬號提取,建議使用非root賬戶啟動redis服務; #使用創建的賬號執行腳本文件,對服務啟動/關閉,賬號需要有執行shell的權限。 [root@node1 ~]# groupadd redis [root@node1 ~]# useradd -g redis -s /bin/bash redis [root@node1 ~]# chown -R redis:redis /usr/local/redis [root@node1 ~]# chown redis:redis /etc/init.d/redis_6379 [root@node1 ~]# chown redis:redis /etc/init.d/redis_6380 [root@node1 ~]# chmod +x /etc/init.d/redis_6379 #默認有執行權限,可不設置 [root@node1 ~]# chmod +x /etc/init.d/redis_6380
6. 設置iptables
[root@node1 ~]# vim /etc/sysconfig/iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 6379 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 6380 -j ACCEPT [root@node1 ~]# service iptables restart
7. 啟動
#可通過啟動腳本啟動/關閉服務;或采用”service”指令 [root@node1 ~]# su - redis -c "/etc/init.d/redis_6379 start" [root@node1 ~]# su - redis -c "/etc/init.d/redis_6380 start"
8. 驗證
[root@node1 ~]# ps aux | grep redis #注意啟動賬戶 [root@node1 ~]# netstat -tunlp | grep redis
三.配置redis cluster
Redis 3.0之后,官方版本支持了Cluster,與之前的第三方cluster相比(如Twenproxy、Codis),Redis Cluster沒有使用Porxy的模式來連接集群節點,而是使用無中心節點的模式來組建集群。
在Cluster出現之前,只有Sentinel保證了Redis的高可用性。
Redis Cluster在多個節點之間進行數據共享,即使部分節點失效或無法進行通訊時,Cluster仍然可以繼續處理請求。
如果每個主節點都有一個從節點支持,在主節點下線或無法與集群的大多數節點進行通訊的情況下,從節點提升為主節點,並提供服務,保證Cluster正常運行。
Redis Cluster的節點分片是通過哈希槽(hash slot)實現的,每個鍵都屬於這 16384(0~16383) 個哈希槽的其中一個,每個節點負責處理一部分哈希槽。
1. 配置集群管理工具
#redis作者基於ruby寫了集群管理工具,首先安裝ruby相關依賴。 [root@node1 ~]# yum install ruby rubygems -y [root@node1 ~]# cd /usr/local/src/ [root@node1 src]# wget https://rubygems.org/downloads/redis-3.3.3.gem [root@node1 src]# gem install redis-3.3.3.gem
#集群管理工具redis-trib.rb在解壓包中已包含; #redis-trib.rb是基於ruby的集群管理工具,如果不使用此工具,也可以手工創建集群,具體操作請見:http://www.cnblogs.com/zhoujinyi/p/6477133.html [root@node1 src]# cp /usr/local/src/redis-4.0.0/src/redis-trib.rb /usr/local/redis/bin/ [root@node1 src]# ln -s /usr/local/redis/bin/redis-trib.rb /usr/local/bin/ redis-trib.rb具有以下功能: 1) create:創建集群 2) check:檢查集群 3) info:查看集群/節點信息 4) fix:修復集群 5) reshard:在線遷移slot 6) rebalance:平衡集群節點slot數量 7) add-node:將新節點加入集群 8) del-node:從集群中刪除節點 9) set-timeout:設置集群節點間心跳連接的超時時間 10) call:在集群全部節點上執行命令 11) import:將外部redis數據導入集群
2. 創建集群
1)創建集群
[root@node1 ~]# redis-trib.rb create --replicas 1 10.11.4.201:6379 10.11.4.201:6380 10.11.4.202:6379 10.11.4.202:6380 10.11.4.203:6379 10.11.4.203:6380
- 節點已自動分配主從關系,且主節點分布在不同的宿主機上,10.11.4.201與10.11.4.202節點上的實例互為主從,但10.11.4.203上的兩個實例互為主從有一定的風險;
- 同意分配計划,輸入"yes"后,各節點開始通訊,並協商哈希槽的分配,最后輸出報告指出各主節點分配的哈希槽,16384個哈希槽全部分配完畢,集群創建成功。
2)注意事項
- (1) "--replicas 1"參數,指定每個主節點配置的從節點數量,如果這里設置為1,最少3個主節點的情況下,總節點數不能低於6,否則集群不能成功創建;
-
(2) 加入集群的節點必須是空節點,不包含槽/數據信息,否則不能加入集群,報錯如下:
[ERR] Node 10.11.4.201:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
解決方案如下:
- 停止redis服務;
- 刪除相關或全部節點的appendonly.aof,dump.rdb,nodes.conf文件;
- 一般執行上面兩步即可,如果還不行可執行清庫操作
-
[root@node1 ~]# redis-cli -c -p 6379 127.0.0.1:6379> flushdb
-
(3) 在同意集群分配方案,輸入"yes"后,進行到"Waiting for the cluster to join"階段,但一直處於等待狀態,無報錯,現象如下:
原因:redis集群除提供給客戶端連接的端口外,還有集群通訊的"集群總線端口",此端口為客戶端連接端口+10000,這里即為:16379/16380
解決方案如下:
- iptables放行相關端口或關閉iptables服務。
3)集群創建流程
-
(1) 為每個節點創建ClusterNode對象,包括連接每個節點,檢查每個節點是否為獨立且db為空的節點,執行load_info方法導入節點信息。
-
(2) 檢查傳入的master節點數量是否大於等於3個,只有大於3個節點才能組成集群。
-
(3) 計算每個master需要分配的slot數量,同時為master分配slave,分配的算法大致如下:
- (a) 將節點按host分類,這樣保證master節點能分配到更多的主機中。
- (b) 不停遍歷host列表,從每個host列表中彈出一個節點,放入interleaved數組,直到所有的節點都彈出為止;master節點列表就是interleaved前面的master數量的節點列表,保存為masters數組。
- (c) 計算每個master節點負責的slot數量,保存在slots_per_node對象,用slot總數除以master數量取整即可。
- (d) 遍歷masters數組,每個master分配slots_per_node個slot,最后一個master,分配到16384個slot為止。
-
(e) 為master分配slave,分配算法會盡量保證master和slave節點不在同一台主機上;對於分配完指定slave數量的節點,如果還有多余的節點,也會為這些節點尋找master。
分配算法會遍歷兩次masters數組:第一次遍歷masters數組,在余下的節點列表找到replicas數量個slave,每個slave為第一個和master節點host不一樣的節點,如果沒有不一樣的節點,則直接取出余下列表的第一個節點;第二次遍歷是在對於節點數除以replicas不為整數,則會多余一部分節點,遍歷的方式跟第一次一樣,只是第一次會一次性給master分配replicas數量個slave,而第二次遍歷只分配一個,直到余下的節點被全部分配出去。
-
(4) 打印出分配信息,確認是否按照給出分配方式創建集群。
-
(5) 輸入"yes"同意后,執行flush_nodes_config操作,該操作執行前面的分配結果,給master分配slot,讓slave復制master;對於還沒有握手(cluster meet)的節點,slave復制操作無法完成,但flush_nodes_config操作出現異常會很快返回,后續握手后會再次執行flush_nodes_config。
-
(6) 為每個節點分配epoch,遍歷節點,每個節點分配的epoch比之前節點大1。
-
(7) 節點間開始相互握手,握手的方式為節點列表的其他節點與第一個節點握手。
-
(8) 然后每隔1秒檢查一次各節點是否已經消息同步完成,使用ClusterNode的get_config_signature方法,檢查的算法為獲取每個節點cluster nodes信息,排序每個節點,組裝成node_id1:slots|node_id2:slot2|...的字符串,如果每節點獲得字符串都相同,即認為握手成功。
-
(9) 再執行一次flush_nodes_config,這次主要是為了完成slave復制操作。
-
(10) 最后再執行check_cluster,全面檢查一次集群狀態;包括和前面握手時檢查一樣的方式再檢查一遍,確認沒有遷移的節點,確認所有的slot都被分配出去了。
-
(11) 至此完成了整個創建流程,返回"[OK] All 16384 slots covered."。
四.驗證
1. 集群檢查
1)redus-trib.rb
#首先列出檢查點的狀態信息,節點10.11.4.203:6379是主節點,分配的哈希槽是10923~16383,有1個從節點運行; #其他節點的信息也會列出,最后檢查哈希槽的分配狀態; #“redis-trib.rb info 10.11.4.201:6379”可以查看單節點信息 [root@node1 ~]# redis-trib.rb check 10.11.4.203:6379
2)CLUSTER命令
#”cluster info”,列出集群狀態,16384個哈希槽已全部分配等; #”cluster nodes”,分別列出每個節點的集群id,狀態等。 [root@node1 ~]# redis-cli -c -h 10.11.4.202 -p 6379 10.11.4.202:6379> cluster info 10.11.4.202:6379> cluster nodes
#”cluster slots”可以查看槽位與對應節點的信息 [root@node1 ~]# redis-cli -c -h 10.11.4.202 -p 6379 10.11.4.202:6379> cluster slots
2. 數據測試
具體數據測試,failover可參考:http://www.cnblogs.com/zhoujinyi/p/6477133.html
1)Key-Value建立與查詢
[root@node1 ~]# redis-cli -c -h 10.11.4.202 -p 6379 10.11.4.202:6379> set test world 10.11.4.202:6379> get test 10.11.4.202:6379> exit
#在節點10.11.4.203:6379上獲取key值”test”,會有一個重定向動作,將”test”的value指向6918哈希槽,在10.11.4.202:6379節點上 [root@node1 ~]# redis-cli -c -h 10.11.4.203 -p 6379 10.11.4.203:6379> get test
#”cluster countkeysinslot XXX”可以獲取指定哈希槽的key的數量 [root@node1 ~]# redis-cli -c -h 10.11.4.202 -p 6379 10.11.4.202:6379> cluster countkeysinslot 6918
2)在線遷移
#在線遷移可用於將集群中的一些slot從源節點遷移到目的節點,如完成集群的在線橫向擴容或縮容。 [root@node1 ~]# redis-trib.rb reshard 10.11.4.202:6379 #遷移槽位數量 How many slots do you want to move (from 1 to 16384)? 1 #遷移目的節點 What is the receiving node ID? 20a918f42f6dbad7feb1d4bbf19d9895c06db691 #遷移源節點 Source node #1:f7fcd0b8170c316c37a947cbfd5a654f9fb582e0 Source node #2:done #遷移計划執行與否 Do you want to proceed with the proposed reshard plan (yes/no)? yes
#哈希槽5461已從10.11.4.202節點遷移到10.11.4.203節點; #遷移后,哈希槽分布不均,可采用redis-trib.rb的“rebalance”進行平衡 [root@node1 ~]# redis-cli -c -h 10.11.4.203 -p 6379 10.11.4.203:6379> cluster nodes