1 redis集群簡介
1.1 集群的概念
所謂的集群,就是通過添加服務器的數量,提供相同的服務,從而讓服務器達到一個穩定、高效的狀態。
1.1.1 使用redis集群的必要性
問題:我們已經部署好了redis,並且能啟動一個redis,實現數據的讀寫,為什么還要學習redis集群?
答:(1)單個redis存在不穩定性。當redis服務宕機了,就沒有可用的服務了。
(2)單個redis的讀寫能力是有限的。
總結:redis集群是為了強化redis的讀寫能力。
1.1.2 如何學習redis集群
--說明:(1)redis集群中,每一個redis稱之為一個節點。
(2)redis集群中,有兩種類型的節點:主節點(master)、從節點(slave)。
(3)redis集群,是基於redis主從復制實現。
所以,學習redis集群,就是從學習redis主從復制模型開始的。
2 redis主從復制
2.1 概念
主從復制模型中,有多個redis節點。
其中,有且僅有一個為主節點Master。從節點Slave可以有多個。
只要網絡連接正常,Master會一直將自己的數據更新同步給Slaves,保持主從同步。
![]()
|
2.2 特點
(1)主節點Master可讀、可寫.
(2)從節點Slave只讀。(read-only)
因此,主從模型可以提高讀的能力,在一定程度上緩解了寫的能力。因為能寫仍然只有Master節點一個,可以將讀的操作全部移交到從節點上,變相提高了寫能力。
2.3 基於配置實現
2.3.1 需求
主節點 |
6380 |
從節點(兩個) |
6381、6382 |
2.3.2 配置步驟
(1)在/usr/local目錄下,創建一個/redis/master-slave目錄
[root@node0719 local]# mkdir -p redis/master-slave |
(2)在master-slave目錄下,創建三個子目錄6380、6381、6382
[root@node0719 master-slave]# mkdir 6380 6381 6382 |
(3)依次拷貝redis解壓目錄下的redis.conf配置文件,到這三個子目錄中。
[root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6380/ [root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6381/ [root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6382/ |
(4)進入6380目錄,修改redis.conf,將port端口修改成6380即可。
[root@node0719 master-slave]# cd ./6380 [root@node0719 6380]# vim redis.conf |
(5)進入6381目錄,修改redis.conf,將port端口改成6381,同時指定開啟主從復制。
[root@node0719 6380]# cd ../6381 [root@node0719 6381]# vim redis.conf |
(6)進入6382目錄,修改redis.conf,將port端口改成6382,同時指定開啟主從復制。
[root@node0719 6380]# cd ../6382 [root@node0719 6381]# vim redis.conf
|
2.3.3 測試
(1)打開三個xshell窗口,在每一個窗口中,啟動一個redis節點。查看日志輸出。(不要改成后台模式啟動,看不到日志,不直觀)
cd /usr/local/redis/master-slave/
cd 6381 && /usr/local/bin/redis-server ./redis.conf
[root@node0719 master-slave]# cd 6380 && redis-server ./redis.conf |
[root@node0719 master-slave]# cd 6381 && redis-server ./redis.conf |
[root@node0719 master-slave]# cd 6382 && redis-server ./redis.conf |
(2)另外再打開三個xshell窗口,在每一個窗口中,登陸一個redis節點
[root@node0719 ~]# redis-cli -p 6380 |
[root@node0719 ~]# redis-cli -p 6381 |
[root@node0719 ~]# redis-cli -p 6382 |
(3)在主節點6380上,進行讀寫操作,操作成功
[root@node0719 ~]# redis-cli -p 6380 127.0.0.1:6380> set user:name zs OK 127.0.0.1:6380> get user:name "zs" 127.0.0.1:6380> |
(4)在從節點6381上
讀操作執行成功,並且成功從6380上同步了數據
[root@node0719 ~]# redis-cli -p 6381 127.0.0.1:6381> get user:name "zs" |
寫操作執行失敗。(從節點,只能讀,不能寫)
127.0.0.1:6381> set user:age 18 (error) READONLY You can't write against a read only slave. |
3 Sentinel哨兵模式
3.1 主從模式的缺陷
當主節點宕機了,整個集群就沒有可寫的節點了。
由於從節點上備份了主節點的所有數據,那在主節點宕機的情況下,如果能夠將從節點變成一個主節點,是不是就可以解決這個問題了呢?
答:是的,這個就是Sentinel哨兵的作用。
3.2 哨兵的任務
Redis 的 Sentinel 系統用於管理多個 Redis 服務器(instance), 該系統執行以下三個任務:
監控(Monitoring): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。
提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
自動故障遷移(Automatic failover): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會進行選舉,將其中一個從服務器升級為新的主服務器, 並讓失效主服務器的其他從服務器改為復制新的主服務器; 當客戶端試圖連接失效的主服務器時, 集群也會向客戶端返回新主服務器的地址, 使得集群可以使用新主服務器代替失效服務器。
3.2.1 監控(Monitoring)
(1)Sentinel可以監控任意多個Master和該Master下的Slaves。(即多個主從模式)
(2)同一個哨兵下的、不同主從模型,彼此之間相互獨立。
(3)Sentinel會不斷檢查Master和Slaves是否正常。
|
3.2.2 自動故障切換(Automatic failover)
3.2.2.1 Sentinel網絡
監控同一個Master的Sentinel會自動連接,組成一個分布式的Sentinel網絡,互相通信並交換彼此關於被監視服務器的信息。下圖中,三個監控s1的Sentinel,自動組成Sentinel網絡結構。
|
疑問:為什么要使用sentinel網絡呢?
答:當只有一個sentinel的時候,如果這個sentinel掛掉了,那么就無法實現自動故障切換了。
在sentinel網絡中,只要還有一個sentinel活着,就可以實現故障切換。
3.2.2.2 故障切換的過程
(1)投票(半數原則)
當任何一個Sentinel發現被監控的Master下線時,會通知其它的Sentinel開會,投票確定該Master是否下線(半數以上,所以sentinel通常配奇數個)。
(2)選舉
當Sentinel確定Master下線后,會在所有的Slaves中,選舉一個新的節點,升級成Master節點。
其它Slaves節點,轉為該節點的從節點。
(1)投票 (2)選舉 |
(3)原Master重新上線
當原Master節點重新上線后,自動轉為當前Master節點的從節點。
(3)原master重新上線 |
3.3 哨兵模式部署
3.3.1 需求
前提:已經存在一個正在運行的主從模式。
另外,配置三個Sentinel實例,監控同一個Master節點。
3.3.2 配置Sentinel
(1)在/usr/local目錄下,創建/redis/sentinels/目錄
[root@node0719 local]# mkdir -p redis/sentinels |
(2)在/sentinels目錄下,以次創建s1、s2、s3三個子目錄中
[root@node0719 sentinels]# mkdir s1 s2 s3 |
(3)依次拷貝redis解壓目錄下的sentinel.conf文件,到這三個子目錄中
[root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s1/ [root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s2/ [root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s3/ |
(4)依次修改s1、s2、s3子目錄中的sentinel.conf文件,修改端口,並指定要監控的主節點。(從節點不需要指定,sentinel會自動識別)
S1哨兵配置如下:
![]() |
S2哨兵配置如下:
![]() |
S3哨兵配置如下:
![]() |
(5)再打開三個xshell窗口,在每一個窗口中,啟動一個哨兵實例,並觀察日志輸出
[root@node0719 sentinels]# redis-sentinel ./s1/sentinel.conf |
[root@node0719 sentinels]# redis-sentinel ./s2/sentinel.conf |
[root@node0719 sentinels]# redis-sentinel ./s3/sentinel.conf |
核心日志輸出:
|
3.3.3 測試
(1)先關閉6380節點。發現,確實重新指定了一個主節點
|
(2)再次上線6380節點。發現,6380節點成為了新的主節點的從節點。
|
3.4 結論
Sentinel哨兵模式,確實能實現自動故障切換。提供穩定的服務。
3.5 注意事項
如果哨兵和redis節點不在同一台服務器上,注意IP綁定的問題。
(1)主從模型,所有的節點,使用ip綁定
|
(2)所有的哨兵,也都使用ip去綁定主機
|
(3)所有的哨兵,都是通過主節點的ip,去監控主從模型
|
4 Redis-cluster集群
4.1 哨兵模式的缺陷
在哨兵模式中,仍然只有一個Master節點。當並發寫請求較大時,哨兵模式並不能緩解寫壓力。
我們知道只有主節點才具有寫能力,那如果在一個集群中,能夠配置多個主節點,是不是就可以緩解寫壓力了呢?
答:是的。這個就是redis-cluster集群模式。
4.2 Redis-cluster集群概念
(1)由多個Redis服務器組成的分布式網絡服務集群;
(2)集群之中有多個Master主節點,每一個主節點都可讀可寫;
(3)節點之間會互相通信,兩兩相連;
(4)Redis集群無中心節點。
|
4.3 集群節點復制
|
在Redis-Cluster集群中,可以給每一個主節點添加從節點,主節點和從節點直接遵循主從模型的特性。
當用戶需要處理更多讀請求的時候,添加從節點可以擴展系統的讀性能。
4.4 故障轉移
Redis集群的主節點內置了類似Redis Sentinel的節點故障檢測和自動故障轉移功能,當集群中的某個主節點下線時,集群中的其他在線主節點會注意到這一點,並對已下線的主節點進行故障轉移。
|
集群進行故障轉移的方法和Redis Sentinel進行故障轉移的方法基本一樣,不同的是,在集群里面,故障轉移是由集群中其他在線的主節點負責進行的,所以集群不必另外使用Redis Sentinel。
4.5 集群分片策略
Redis-cluster分片策略,是用來解決key存儲位置的。
集群將整個數據庫分為16384個槽位slot,所有key-value數據都存儲在這些slot中的某一個上。一個slot槽位可以存放多個數據,key的槽位計算公式為:slot_number=crc16(key)%16384,其中crc16為16位的循環冗余校驗和函數。
集群中的每個主節點都可以處理0個至16383個槽,當16384個槽都有某個節點在負責處理時,集群進入上線狀態,並開始處理客戶端發送的數據命令請求。
|
4.6 集群redirect轉向
由於Redis集群無中心節點,請求會隨機發給任意主節點;
主節點只會處理自己負責槽位的命令請求,其它槽位的命令請求,該主節點會返回客戶端一個轉向錯誤;
客戶端根據錯誤中包含的地址和端口重新向正確的負責的主節點發起命令請求。
|
4.7 集群搭建
4.7.1 准備工作
(1)安裝ruby環境
redis集群管理工具redis-trib.rb依賴ruby環境,首先需要安裝ruby環境:
yum -y install ruby yum -y install rubygems |
(2)安裝ruby和redis的接口程序
拷貝redis-3.0.0.gem至/usr/local下,執行安裝:
gem install /usr/local/redis-3.0.0.gem |
4.7.2 集群規划
(1)Redis集群最少需要6個節點,可以分布在一台或者多台主機上。
本教案在一台主機上創建偽分布式集群,不同的端口表示不同的redis節點,如下:
主節點:192.168.56.3:7001 192.168.56.3:7002 192.168.56.3:7003
從節點:192.168.56.3:7004 192.168.56.3:7005 192.168.56.3:7006
(2)在/usr/local/redis下創建redis-cluster目錄,其下創建7001、7002。。7006目錄,如下:
|
(3)將redis解壓路徑下的配置文件redis.conf,依次拷貝到每個700X目錄內,並修改每個700X目錄下的redis.conf配置文件:
必選配置: port 700X bind 192.168.23.3 cluster-enabled yes 建議配置: daemonized yes logfile /usr/local/redis/redis-cluster/700X/node.log |
4.7.3 啟動每個結點redis服務
依次以700X下的redis.conf,啟動redis節點。(必須指定redis.conf文件)
redis-server /usr/local/redis/redis-cluster/700X/redis.conf |
4.7.4 執行創建集群命令
進入到redis源碼存放目錄redis/redis-4.10.3/src下,執行redis-trib.rb,此腳本是ruby腳本,它依賴ruby環境。
./redis-trib.rb create --replicas 1 192.168.159.10:7001 192.168.159.10:7002 192.168.159.10:7003 192.168.159.10:7004 192.168.159.10:7005 192.168.159.10:7006 |
創建過程如下:
|
4.7.5 查詢集群信息
集群創建成功登陸任意redis結點查詢集群中的節點情況。
./redis-cli -c -h 192.168.56.3 -p 7001
|
說明:./redis-cli -c -h 192.168.56.3 -p 7001 ,其中:
-c表示以集群方式連接redis, -h指定ip地址, -p指定端口號
cluster nodes 查詢集群結點信息; cluster info 查詢集群狀態信。 |
4.8 集群管理
4.8.1 添加主節點
4.8.1.1 節點規划
集群創建成功后可以向集群中添加節點,下面是添加一個master主節點
添加7007節點,參考集群結點規划章節添加一個“7007”目錄作為新節點。
添加節點,執行下邊命令:
./redis-trib.rb add-node 192.168.23.3:7007 192.168.23.3:7001
|
查看集群結點發現7007已添加到集群中:
|
4.8.1.2 hash槽重新分配
添加完新的主節點后,需要對主節點進行hash槽分配,這樣該主節才可以存儲數據。
redis集群有16384個槽,被所有的主節點共同分配,通過查看集群結點可以看到槽占用情況。
|
給剛添加的7007結點分配槽:
第一步:連接上集群
./redis-trib.rb reshard 192.168.23.3:7001(連接集群中任意一個可用節點都行) |
第二步:輸入要分配的槽數量
|
輸入 500表示要分配500個槽
第三步:輸入接收槽的結點id
|
這里准備給7007分配槽,通過cluster nodes查看7007結點id為79bbb30bba66b4997b9360dd09849c67d2d02bb9
輸入:79bbb30bba66b4997b9360dd09849c67d2d02bb9
第四步:輸入源結點id
|
這里輸入all,表示從其它主節點中分配。
第五步:輸入yes開始移動槽到目標結點id
|
4.8.2 添加從節點
集群創建成功后可以向集群中添加節點,下面是添加一個slave從節點。
添加7008從結點,將7008作為7007的從結點。
新增從節點命令格式:
./redis-trib.rb add-node --slave --master-id masterID newNodIP:port MasterIP:port
masterID 主節點id,從cluster nodes信息中查看 newNodIP:port 新增節點的ip:端口 MasterIP:port 主節點的ip:端口 |
執行如下命令:
./redis-trib.rb add-node --slave --master-id 909c349f5f2d4db015101fb7c4e3c227a74ad382 192.168.4.253:7008 192.168.4.253:7007 |
79bbb30bba66b4997b9360dd09849c67d2d02bb9 是7007結點的id,可通過cluster nodes查看。
|
注意:
如果原來該結點在集群中的配置信息已經生成cluster-config-file指定的配置文件中(如果cluster-config-file沒有指定則默認為nodes.conf),這時可能會報錯:
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0 |
解決方法:
刪除生成的配置文件nodes.conf,刪除后再執行./redis-trib.rb add-node指令。
查看集群中的結點,剛添加的7008為7007的從節點:
|
4.8.3 刪除節點:
刪除節點命令格式:
./redis-trib.rb del-node nodeIP:port nodeID nodeIP:port 待刪除節點的ip:端口 nodeID 待刪除節點的id,從cluster node中查看 |
注意,刪除已經占有hash槽的結點會失敗,報錯如下:
[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again. |
需要將該結點占用的hash槽分配出去(參考hash槽重新分配章節)。
5 java程序連接redis集群
5.1 連接步驟
5.1.1 第一步:創建項目,導入jar包
|
5.1.2 第二步:創建redis集群的客戶端
package cn.gzsxt.jedis.test;
import java.util.HashSet; import java.util.Set; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster;
public class TestJedisCluster {
public static void main(String[] args) { //1、創建jedidsCluster客戶端 //創建一個set集合,用來封裝所有redis節點的信息 Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.23.12", 7001)); nodes.add(new HostAndPort("192.168.23.12", 7002)); nodes.add(new HostAndPort("192.168.23.12", 7003)); nodes.add(new HostAndPort("192.168.23.12", 7004)); nodes.add(new HostAndPort("192.168.23.12", 7005)); nodes.add(new HostAndPort("192.168.23.12", 7006)); nodes.add(new HostAndPort("192.168.23.12", 7007)); nodes.add(new HostAndPort("192.168.23.12", 7008));
JedisCluster cluster = new JedisCluster(nodes);
String name = cluster.get("user:id:1:name"); cluster.set("user:id:1:address", "廣東省廣州市廣州尚學堂"); String address = cluster.get("user:id:1:address");
System.out.println("name:"+name); System.out.println("address:"+address); if(null!=cluster){ cluster.close(); } } } |
5.2 注意事項:
連接Redis集群時,需要修改防火牆,開方每一個redis節點的端口。
說明:如果要開發一個范圍的端口,可以使用冒號來分割,即: 7001:7008,表示開發7001-7008之間所有的端口
|
5.3 測試
|
訪問Redis-cluster集群成功!!!