1. 為什麼需要叢集-高可用性
-
生產環境的實際需求和問題:
-
容量不夠,redis 如何進行擴容。
-
併發寫操作,redis 如何分攤。
-
主從模式,薪火相傳模式,主機宕機,會導致 ip 地址發生變化,應用程式中配置需要修改對應的主機地址,埠等資訊。
-
傳統解決方案 代理主機來解決
客戶端請求先到代理伺服器 由代理伺服器進行請求轉發到對應的業務處理器 為了高可用,代理服務,A服務,B服務,C服務都需要搭建主從結構(至少是一主一從 這樣就需求搭建至少 8 臺伺服器)。 這種方案的缺點是:成本高,維護困難,如果是一主多從,成本就會更高。

-
各個 Redis 服務仍然採用主從結構。
-
各個 Redis 服務是連通的,任何一臺伺服器,都可以作為請求入口 。
-
各個 Redis 伺服器因為是連通的,可以進行請求轉發
-
這種方式,就無中心化 叢集配置,可以看到,只需要 6 臺伺服器即可搞定。
-
無中心化叢集配置 ,還會根據 key 值,計算 slot ,把資料分散到不同的主機,從而緩解單個主機的存取壓力
-
Redis 推薦使用無中心化叢集配置。
-
在實際生成環境,各個 Redis 伺服器,應當部署到不同的機器(防止機器宕機,主從複製失效)。
2. 叢集概述(及其搭建)
-
Redis 叢集實現了對 Redis 的水平擴容,即啟動 N 個 Redis 節點,將整個資料庫分佈儲存在這個 N 個節點中,每個節點儲存總數居的 1 / N
-
Redis 叢集透過分割槽(partition) 來提供一定程度的可用性(availability) ,即使叢集中有一部分節點失效或者無法進行通訊,叢集也可以繼續處理命令請求。

-
redis.conf
配置修改
cluster-enabledyes 開啟叢集模式
cluster-config-filenodes-6379.conf 設定節點配置檔名
cluster-node-timeout15000 設定節點失聯時間,超過該時間(毫秒),叢集自動進行主 從切換
-
vi /rainbowsea/redis6379.conf
, 刪除不必要的內容 增加 cluster 配置, 檔案最後內容,如圖
include/rainbowsea/redis.conf
pidfile"/var/run/redis_6379.pid"
port6379
dbfilename"dump6379.rdb"
masterauthrainbowsea
cluster-enabledyes
cluster-config-filenodes-6379.conf
cluster-node-timeout15000


[root@localhost rainbowsea]
# cp redis6379.conf redis6380.conf
[root@localhost rainbowsea]
# cp redis6379.conf redis6381.conf
[root@localhost rainbowsea]
# cp redis6379.conf redis6389.conf
[root@localhost rainbowsea]
# cp redis6379.conf redis6390.conf
[root@localhost rainbowsea]
# cp redis6379.conf redis6391.conf
[root@localhost rainbowsea]
#


-
使用查詢替換修改另外 5 個檔案


換指令 :%s/6379/6380
其它幾個檔案以此操作即可, 操作的時候,一定要小心, 最後建議再檢查一下

masterauth rainbowsea
加上 Redis 的密碼,沒有設定密碼的則不用配置這個。masterauth rainbowsea
加上 Redis 的密碼,沒有設定密碼的則不用配置這個。masterauth rainbowsea
加上 Redis 的密碼,沒有設定密碼的則不用配置這個。include/rainbowsea/redis.conf
pidfile"/var/run/redis_6379.pid"
port6379
dbfilename"dump6379.rdb"
masterauthrainbowsea
cluster-enabledyes
cluster-config-filenodes-6379.conf
cluster-node-timeout15000
-
啟動 6 個 Redis 服務
[root@localhost rainbowsea]
# redis-server /rainbowsea/redis6379.conf
[root@localhost rainbowsea]
# redis-server /rainbowsea/redis6380.conf
[root@localhost rainbowsea]
# redis-server /rainbowsea/redis6381.conf
[root@localhost rainbowsea]
# redis-server /rainbowsea/redis6389.conf
[root@localhost rainbowsea]
# redis-server /rainbowsea/redis6390.conf
[root@localhost rainbowsea]
# redis-server /rainbowsea/redis6391.conf
[root@localhost rainbowsea]
# ps -aux | grep redis


-
將六個節點合成一個叢集


redis-cli --cluster create --cluster-replicas 1 192.168.76.147:6379 192.168.76.147:6380 192.168.76.147:6381 192.168.76.147:6389 192.168.76.147:6390 192.168.76.147:6391
redis-cli --cluster create -a rainbowsea --cluster-replicas 1 192.168.76.147:6379 192.168.76.147:6380 192.168.76.147:6381 192.168.76.147:6389 192.168.76.147:6390 192.168.76.147:6391
-
組合之前,確保所有(你要使用上的埠的) Redis伺服器都是啟動的,同時在 root 目錄下(我這裡是 root 配置的)
nodes-xxxx.conf
檔案都生成正常。 -
此時不可以用 127.0.0.1 ,需要使用真實的 IP地址(就是你連線 Linux 的地址,Linux 當中使用
ifconfig
指令查詢到的地址),在真實生產環境 IP都是獨立的。 -
replicas 1
採用最簡單的方式配置叢集,一臺主機,一臺從機,正好三組。 -
搭建加群如果沒有成功,把
sentinel
程序關閉掉,再試一下。 -
分許主從對應關係。


-
分析主從對應關係:如下


-
叢集方式登入:
[root@localhost src]
# redis-cli -c -p 6379
127.0.0.1:6379> auth rainbowsea
127.0.0.1:6379> cluster nodes


[root@localhost src]
# redis-cli -c -p 6379

-
一個叢集至少要有三個主節點。
-
選項
--cluster-replicas 1
表示我們希望為叢集中的每個主節點建立一個從節點。 -
分配原則: 儘量保證主伺服器和從伺服器各自執行在不同的 IP 地址(機器),防止機器故障導致主從機制失效,高可用性得不到保障。
3. Redis 叢集的使用


-
一個 Redis 叢集包含了
16384
個插槽(hash slot) ,編號從0-16383
,Redis 中的每個鍵都屬於這 16384 個插槽的其中一個。注意:這裡雖然只有 16384個插槽,但是並不是只能插入 16384個鍵,多個不同的鍵可以插入到同一個插槽的,並不是一個插槽一個鍵的 。 -
叢集使用公式
CRC16(key) % 16384
來計算鍵 key 屬於哪個槽,其中 CRC16(key) 語句用於計算鍵 key 的 CRC16的校驗和

-
叢集中的每個節點負責處理一部分插槽。舉個例子:如果一個叢集可以有主節點,其中
節點 A 負責處理0號 ~ 5460號
插槽 節點 B 負責處理5461號 ~ 10922號
插槽 節點 C 負責處理10923號 ~ 16383號
插槽
-
在 Redis 每次錄入,查詢鍵值,redis 都會計算出該 key 應該送往的插槽,如果不是該客戶端對應伺服器的插槽,redis 會告知前往的 Redis 例項地址和埠。
-
Redis-cli 客戶端提供了
-c
引數實現自動重定向。 -
如
redis-cli -c -p 6379
登入後,再錄入,查詢鍵值對可以自動重定向




-
不在一個 slot 下的鍵值,是不能使用 mget,mset 等多鍵操作。
192.168.76.147:6381> mset k1
"v1"
k2
"v2"
k3
"v3"


-
可以透過
{}
來定義組的概念,從而使 key 中{}
內相同內容的鍵值對放到一個slot
中去,就解決了上面 mget 分佈到不同 slot 而導致失敗的原因。
192.168.76.147:6381> mest k1{order}
"v1"
k2{order}
"v2"
k3{order}
"v3"

注意:你如果對鍵加上了{}組,那麼你想要獲取到該值的時候,也是要加上對應的{}組的,才能獲取到的。



-
指令:
CLUSTER KEYSLOT <key>
返回 key 對應的 slot 值
192.168.76.147:6381> cluster keyslot k1

192.168.76.147:6381> cluster keyslot k2{order}

可以看到歸屬於{}
同一組的,Redis都是分配到了同一個 slot 插槽數值當中。
-
指令:
CLUSTER COUNTKEYSINSLOT <slot>
返回 slot 有多少個 key
192.168.76.147:6381> cluster countkeysinslot 12706
(
integer
) 1
192.168.76.147:6381> cluster countkeysinslot 16025
(
integer
) 3

-
指令:
CLUSTER GETKEYSINSLOT <slot><count>
返回 count 個 slot 槽中的鍵
192.168.76.147:6381> cluster getkeysinslot 16025 1
1)
"k1{order}"
192.168.76.147:6381> cluster getkeysinslot 16025 2
1)
"k1{order}"
2)
"k2{order}"
192.168.76.147:6381> cluster getkeysinslot 16025 3


4. Redis 叢集故障恢復
-
如果主節點下線, 從節點會自動升為主節點(注意 15 秒超時, 再觀察比較準確)
[root@localhost ~]
# redis-cli -c -p 6380





-
主節點恢復後,主節點回來變成從機


主從節點都宕掉了
,Redis 服務是否還能繼續,要根據不同的配置而言。
如果某一段插槽的主從 都宕機了,而在 redis.conf 配置檔案當中cluster-require-full-coverage
為yes
,那麼,整個叢集都會被宕掉,無法使用。 如果某一段插槽的主從 都宕機了,而在 redis.conf 配置檔案當中cluster-require-full-coverage
為no
,那麼,僅僅只是該段插槽的資料不能使用了,也無法儲存了,其他插槽的資料還可以繼續使用。 redis.conf 檔案當中的引數cluster-require-full-coverage
5. Redis 叢集的 Jedis 開發(使用Java程式連線 Redis 同時開啟叢集)
-
即使連線的不是主機,叢集會自動切換主機進行儲存,主機寫,從機讀 。
-
無中心化主從叢集,無論從哪臺主機寫的資料,其他主機上都能讀到資料。
-
注意:需要將 Redis 相關的埠都開啟 否則會報錯
[root@localhost src]
# firewall-cmd --add-port=6379/tcp --permanent
Warning: ALREADY_ENABLED: 6379:tcp
success
[root@localhost src]
# firewall-cmd --add-port=6380/tcp --permanent
success
[root@localhost src]
# firewall-cmd --add-port=6381/tcp --permanent
success
[root@localhost src]
# firewall-cmd --add-port=6389/tcp --permanent
success
[root@localhost src]
# firewall-cmd --add-port=6390/tcp --permanent
success
[root@localhost src]
# firewall-cmd --add-port=6391/tcp --permanent

[root@localhost src]
# firewall-cmd --reload

[root@localhost src]
# firewall-cmd --list-all

pom.xml
當中引入 redis.clients
依賴。如下:<!-- 引入 jedis 依賴-->
<
dependency
>
<
groupId
>redis.clients</
groupId
>
<
artifactId
>jedis</
artifactId
>
<
version
>3.2.0</
version
>
</
dependency
>


package
com.rainbowsea.jedis;
import
org.junit.Test;
import
redis.clients.jedis.Jedis;
publicclassJedisCluster_
{
@Test
publicvoidcon
() {
// 使用 ip地址 + redis的埠的構造器方法
Jedisjedis=newJedis
(
"192.168.76.147"
,
6379
);
// 如果Redis 配置了密碼,則需要進行身份校驗
jedis.auth(
"rainbowsea"
);
Stringping=
jedis.ping();
System.out.println(
"連線成功 ping 返回的結果 = "
+ ping);
jedis.close();
// 關閉當前連線,注意並沒有關閉 Redis
}
}


import
org.junit.Test;
import
redis.clients.jedis.HostAndPort;
import
redis.clients.jedis.Jedis;
import
redis.clients.jedis.JedisCluster;
import
redis.clients.jedis.JedisPoolConfig;
import
java.util.HashSet;
import
java.util.Set;
publicclassJedisCluster_
{
publicstaticvoidmain
(String[] args) {
Set<HostAndPort> set =
newHashSet
<>();
set.add(
newHostAndPort
(
"192.168.76.147"
,
6379
));
JedisPoolConfigjedisPoolConfig=newJedisPoolConfig
();
// 對連線池進行配置
jedisPoolConfig.setMaxTotal(
200
);
jedisPoolConfig.setMaxIdle(
32
);
jedisPoolConfig.setMaxWaitMillis(
60
*
1000
);
// 單位是毫秒
jedisPoolConfig.setBlockWhenExhausted(
true
);
jedisPoolConfig.setTestOnBorrow(
true
);
JedisClusterjedisCluster=newJedisCluster
(set,
5000
,
5000
,
5
,
"rainbowsea"
,jedisPoolConfig );
jedisCluster.set(
"address"
,
"bj"
);
Stringaddress=
jedisCluster.get(
"address"
);
System.out.println(
"address=>"
+ address);
jedisCluster.close();
}
}



6. Redis 叢集的優缺點
-
實現擴容。
-
分攤壓力。
-
無中心配置相對簡單。
-
多鍵操作是不被支援的。
-
多鍵的 Redis 事務是不被支援的。 lua 指令碼不被支援
-
由於叢集方案出現較晚,很多公司已經採用了其他的叢集方案,而其它方案想要遷移至 redis cluster ,需要整體遷移而不是逐步過渡,複雜度較大。
7. 補充:

[root@localhost ~]
# rm -f dump*.rdb




8. 最後:
“在這個最後的篇章中,我要表達我對每一位讀者的感激之情。你們的關注和回覆是我創作的動力源泉,我從你們身上吸取了無盡的靈感與勇氣。我會將你們的鼓勵留在心底,繼續在其他的領域奮鬥。感謝你們,我們總會在某個時刻再次相遇。”