天天看點

redis分布式叢集

文章目錄

      • 一、redis持久化
        • 1.1.RDB持久化
          • 1.1.1.執行時機
          • 1.1.2.RDB原理
          • 1.1.3.小結
        • 1.2.AOF持久化
          • 1.2.1.AOF原理
          • 1.2.2.AOF配置
          • 1.2.3.AOF檔案重寫
          • 1.2.4.小結
        • 1.3.RDB與AOF對比
      • 二、Redis主從叢集
        • 2.1.叢集結構
        • 2.2.準備執行個體和配置
        • 2.3.啟動
        • 2.4.開啟主從關系
        • 2.5.測試
        • 2.6.主從資料同步原理
          • 2.6.1.全量同步
          • 2.6.2.增量同步
          • 2.2.3.repl_backlog原理
        • 2.7.主從同步優化
        • 2.8.小結
      • 三、搭建哨兵叢集
        • 3.1.哨兵原理
          • 3.1.1.叢集結構和作用
          • 3.1.2.叢集監控原理
          • 3.1.3.叢集故障恢複原理
          • 3.1.4.小結
        • 3.2.準備執行個體和配置
        • 3.3.啟動
        • 3.4.測試
        • 3.5.RedisTemplate
          • 3.5.1.引入依賴
          • 3.5.2.配置Redis位址
          • 3.5.3.配置讀寫分離
          • 3.5.4.測試
      • 四、Redis分片叢集
        • 4.1.搭建分片叢集
          • 4.1.1.準備執行個體和配置
          • 4.1.2.啟動
          • 4.1.3.建立叢集
        • 4.2.散列插槽
          • 4.2.1.插槽原理
          • 4.2.2.小結
        • 4.3.叢集伸縮
          • 4.3.1.需求分析
          • 4.3.2.建立新的redis執行個體
          • 4.3.3.添加新節點到redis
          • 4.3.4.轉移插槽
        • 4.4.故障轉移
          • 4.4.1.自動故障轉移
          • 4.4.2.手動故障轉移
        • 4.5.RedisTemplate通路分片叢集

一、redis持久化

Redis有兩種持久化方案:

  • RDB持久化
  • AOF持久化

1.1.RDB持久化

RDB全稱Redis Database Backup file(Redis資料備份檔案),也被叫做Redis資料快照。簡單來說就是把

記憶體中的所有資料都記錄到磁盤

中。當Redis執行個體故障重新開機後,從磁盤讀取快照檔案,恢複資料。快照檔案稱為RDB檔案,預設是儲存在目前運作目錄。

1.1.1.執行時機

RDB持久化在四種情況下會執行:

  • 執行save指令
  • 執行bgsave指令
  • redis停機時
  • 觸發RDB條件時

1)save指令

執行下面的指令,可以立即執行一次RDB:

redis分布式叢集

save指令會導緻主程序執行RDB,這個過程中其它所有指令都會被阻塞。

隻有在資料遷移時可能用到

,資料遷移時redis不再接收新的請求,使用save可以阻塞請求。

2)bgsave指令

下面的指令可以異步執行RDB:

redis分布式叢集

這個指令執行後會開啟獨立程序完成RDB,主程序可以持續處理使用者請求,不受影響。

3)停機時

redis停機時會執行一次save指令,實作RDB持久化。

4)觸發RDB條件

Redis内部有觸發RDB的機制,可以在redis.conf檔案中找到,格式如下:

# 900秒内,如果至少有1個key被修改,則執行bgsave , 如果是save "" 則表示禁用RDB
save 900 1  
save 300 10  
save 60 10000 
           

RDB的其它配置也可以在redis.conf檔案中設定:

# 是否壓縮 ,建議不開啟,壓縮也會消耗cpu,磁盤的話不值錢
rdbcompression yes

# RDB檔案名稱
dbfilename dump.rdb  

# 檔案儲存的路徑目錄
dir ./ 
           
1.1.2.RDB原理

bgsave開始時會fork主程序得到子程序,子程序共享主程序的記憶體資料。完成fork後讀取記憶體資料并寫入 RDB 檔案。

fork采用的是copy-on-write技術:

  • 當主程序執行讀操作時,通路共享記憶體;
  • 當主程序執行寫操作時,則會拷貝一份資料,執行寫操作。
    redis分布式叢集
主程序fork()子程序之後,核心把主程序中所有的記憶體頁的權限都設為read-only,然後子程序的位址空間指向主程序。這也就是共享了主程序的記憶體,當其中某個程序寫記憶體時(這裡肯定是主程序寫,因為子程序隻負責rdb檔案持久化工作,不參與用戶端的請求),CPU硬體檢測到記憶體頁是read-only的,于是觸發頁異常中斷(page-fault),陷入核心的一個中斷例程。中斷例程中,核心就會把觸發的異常的頁複制一份(這裡僅僅複制異常頁,也就是所修改的那個資料頁,而不是記憶體中的全部資料),于是主子程序各自持有獨立的一份。主程序修改了哪個資料,指針就指向那個新的資料,其餘指針依舊指向和子程序共享的資料
1.1.3.小結

RDB方式bgsave的基本流程?

  • fork主程序得到一個子程序,共享記憶體空間
  • 子程序讀取記憶體資料并寫入新的RDB檔案
  • 用新RDB檔案替換舊的RDB檔案

RDB會在什麼時候執行?save 60 1000代表什麼含義?

  • 預設是服務停止時
  • 代表60秒内至少執行1000次修改則觸發RDB

RDB的缺點?

  • RDB是間隔一段時間進行持久化,如果持久化之間 redis 發生故障,會發生資料丢失風險
  • fork子程序、壓縮、寫出RDB檔案都比較耗時

1.2.AOF持久化

1.2.1.AOF原理

AOF全稱為Append Only File(追加檔案)。Redis處理的每一個寫指令都會記錄在AOF檔案,可以看做是指令日志檔案。

redis分布式叢集
1.2.2.AOF配置

AOF預設是關閉的,需要修改redis.conf配置檔案來開啟AOF:

# 是否開啟AOF功能,預設是no
appendonly yes
# AOF檔案的名稱
appendfilename "appendonly.aof"
           

AOF的指令記錄的頻率也可以通過redis.conf檔案來配:

# 表示每執行一次寫指令,立即記錄到AOF檔案
appendfsync always 
# 寫指令執行完先放入AOF緩沖區,然後表示每隔1秒将緩沖區資料寫到AOF檔案,是預設方案
appendfsync everysec 
# 寫指令執行完先放入AOF緩沖區,由作業系統決定何時将緩沖區内容寫回磁盤
appendfsync no
           

三種政策對比:

redis分布式叢集
1.2.3.AOF檔案重寫

因為是記錄指令,AOF檔案會比RDB檔案大的多。而且AOF會記錄對同一個key的多次寫操作,但隻有最後一次寫操作才有意義。通過執行bgrewriteaof指令,可以讓AOF檔案執行重寫功能,用最少的指令達到相同效果。

redis分布式叢集

如圖,AOF原本有三個指令,但是

set num 123 和 set num 666

都是對num的操作,第二次會覆寫第一次的值,是以第一個指令記錄下來沒有意義。

是以重寫指令後,理論上AOF檔案内容就是:

mset name jack num 666

,實際上是一堆亂碼

redis分布式叢集

Redis也會在觸發門檻值時自動去重寫AOF檔案。門檻值也可以在redis.conf中配置:

# AOF檔案比上次檔案 增長超過多少百分比則觸發重寫
auto-aof-rewrite-percentage 100
# AOF檔案體積最小多大以上才觸發重寫 
auto-aof-rewrite-min-size 64mb 
           
1.2.4.小結

AOF的缺點?

  • 比如在同樣資料規模的情況下,AOF檔案要比RDB檔案的體積大。而且,AOF 方式的恢複速度也要慢于RDB 方式。

1.3.RDB與AOF對比

RDB和AOF各有自己的優缺點,如果對資料安全性要求較高,在實際開發中往往會結合兩者來使用。

redis分布式叢集

二、Redis主從叢集

2.1.叢集結構

我們搭建的主從叢集結構如圖:

redis分布式叢集

共包含三個節點,一個主節點,兩個從節點。

這裡我們會在同一台虛拟機中開啟3個redis執行個體,模拟主從叢集,資訊如下:

IP PORT 角色
192.168.150.101 7001 master
192.168.150.101 7002 slave
192.168.150.101 7003 slave

2.2.準備執行個體和配置

要在同一台虛拟機開啟3個執行個體,必須準備三份不同的配置檔案和目錄,配置檔案所在目錄也就是工作目錄。

1)建立目錄

我們建立三個檔案夾,名字分别叫7001、7002、7003:

# 進入/tmp目錄
cd /tmp
# 建立目錄
mkdir 7001 7002 7003
           

2)拷貝配置檔案到每個執行個體目錄

然後将redis-6.2.4/redis.conf檔案拷貝到三個目錄中(在/tmp目錄執行下列指令):

# 方式一:逐個拷貝
cp redis-6.2.4/redis.conf 7001
cp redis-6.2.4/redis.conf 7002
cp redis-6.2.4/redis.conf 7003

# 方式二:管道組合指令,一鍵拷貝
echo 7001 7002 7003 | xargs -t -n 1 cp redis-6.2.4/redis.conf
           

3)修改每個執行個體的端口、工作目錄

修改每個檔案夾内的配置檔案,将端口分别修改為7001、7002、7003,将rdb檔案儲存位置都修改為自己所在目錄(在/tmp目錄執行下列指令):

sed -i -e 's/6379/7001/g' -e 's/dir .\//dir \/tmp\/7001\//g' 7001/redis.conf
sed -i -e 's/6379/7002/g' -e 's/dir .\//dir \/tmp\/7002\//g' 7002/redis.conf
sed -i -e 's/6379/7003/g' -e 's/dir .\//dir \/tmp\/7003\//g' 7003/redis.conf
           

4)修改每個執行個體的聲明IP

虛拟機本身有多個IP,為了避免将來混亂,我們需要在redis.conf檔案中指定每一個執行個體的綁定ip資訊,格式如下:

# redis執行個體的聲明 IP
replica-announce-ip 192.168.150.101
           

每個目錄都要改,我們一鍵完成修改(在/tmp目錄執行下列指令):

# 逐一執行
sed -i '1a replica-announce-ip 192.168.150.101' 7001/redis.conf
sed -i '1a replica-announce-ip 192.168.150.101' 7002/redis.conf
sed -i '1a replica-announce-ip 192.168.150.101' 7003/redis.conf

# 或者一鍵修改
printf '%s\n' 7001 7002 7003 | xargs -I{} -t sed -i '1a replica-announce-ip 192.168.150.101' {}/redis.conf
           

2.3.啟動

為了友善檢視日志,我們打開3個ssh視窗,分别啟動3個redis執行個體,啟動指令:

# 第1個
redis-server 7001/redis.conf
# 第2個
redis-server 7002/redis.conf
# 第3個
redis-server 7003/redis.conf
           

啟動後:

redis分布式叢集

如果要一鍵停止,可以運作下面指令:

2.4.開啟主從關系

現在三個執行個體還沒有任何關系,要配置主從可以使用replicaof 或者slaveof(5.0以前)指令。

有臨時和永久兩種模式:

  • 修改配置檔案(永久生效)
    • 在redis.conf中添加一行配置:

      slaveof <masterip> <masterport>

  • 使用redis-cli用戶端連接配接到redis服務,執行slaveof指令(重新開機後失效):

注意:在5.0以後新增指令replicaof,與salveof效果一緻。

通過redis-cli指令連接配接7002,執行下面指令:

# 連接配接 7002
redis-cli -p 7002
# 執行slaveof
slaveof 192.168.150.101 7001
           

通過redis-cli指令連接配接7003,執行下面指令:

# 連接配接 7003
redis-cli -p 7003
# 執行slaveof
slaveof 192.168.150.101 7001
           

然後連接配接 7001節點,檢視叢集狀态:

# 連接配接 7001
redis-cli -p 7001
# 檢視狀态
info replication
           

結果:

redis分布式叢集

注意:在linxu伺服器搭建主從複制報錯

redis分布式叢集

解決方案:

  • 去雲伺服器安全組打開7001,7002,7003端口即可

2.5.測試

執行下列操作以測試:

  • 利用redis-cli連接配接7001,執行

    set num 123

  • 利用redis-cli連接配接7002,執行

    get num

    ,再執行

    set num 666

  • 利用redis-cli連接配接7003,執行

    get num

    ,再執行

    set num 888

可以發現,隻有在7001這個master節點上可以執行寫操作,7002和7003這兩個slave節點隻能執行讀操作。

redis分布式叢集

2.6.主從資料同步原理

2.6.1.全量同步

主從第一次建立連接配接時,會執行全量同步,将master節點的所有資料都拷貝給slave節點,流程:

redis分布式叢集

這裡有一個問題,master如何得知salve是第一次來連接配接呢??

有幾個概念,可以作為判斷依據:

  • Replication Id:簡稱replid,是資料集的标記,id一緻則說明是同一資料集。每一個master都有唯一的replid,slave則會繼承master節點的replid
  • offset:偏移量,随着記錄在repl_baklog中的資料增多而逐漸增大。slave完成同步時也會記錄目前同步的offset。如果slave的offset小于master的offset,說明slave資料落後于master,需要更新。

是以slave做資料同步,必須向master聲明自己的replication id 和offset,master才可以判斷到底需要同步哪些資料。

因為slave原本也是一個master,有自己的replid和offset,當第一次變成slave,與master建立連接配接時,發送的replid和offset是自己的replid和offset。

master判斷發現slave發送來的replid與自己的不一緻,說明這是一個全新的slave,就知道要做全量同步了。

master會将自己的replid和offset都發送給這個slave,slave儲存這些資訊。以後slave的replid就與master一緻了。

是以,master判斷一個節點是否是第一次同步的依據,就是看replid是否一緻。

如圖:

redis分布式叢集

完整流程描述:

  • slave節點請求增量同步
  • master節點判斷replid,發現不一緻,拒絕增量同步
  • master将完整記憶體資料生成RDB,發送RDB到slave
  • slave清空本地資料,加載master的RDB
  • master将RDB期間的指令記錄在repl_baklog,并持續将log中的指令發送給slave
  • slave執行接收到的指令,保持與master之間的同步
2.6.2.增量同步

全量同步需要先做RDB,然後将RDB檔案通過網絡傳輸個slave,成本太高了。是以除了第一次做全量同步,其它大多數時候slave與master都是做增量同步。

什麼是增量同步?就是隻更新slave與master存在差異的部分資料。如圖:

redis分布式叢集

那麼master怎麼知道slave與自己的資料差異在哪裡呢?

2.2.3.repl_backlog原理

master怎麼知道slave與自己的資料差異在哪裡呢?

這就要說到全量同步時的repl_baklog檔案了。

這個檔案是一個固定大小的數組,隻不過數組是環形,也就是說角标到達數組末尾後,會再次從0開始讀寫,這樣數組頭部的資料就會被覆寫。

repl_baklog中會記錄Redis處理過的指令日志及offset,包括master目前的offset,和slave已經拷貝到的offset:

redis分布式叢集

slave與master的offset之間的差異,就是salve需要增量拷貝的資料了。

随着不斷有資料寫入,master的offset逐漸變大,slave也不斷的拷貝,追趕master的offset:

redis分布式叢集

直到數組被填滿:

redis分布式叢集

此時,如果有新的資料寫入,就會覆寫數組中的舊資料。不過,舊的資料隻要是綠色的,說明是已經被同步到slave的資料,即便被覆寫了也沒什麼影響。因為未同步的僅僅是紅色部分。

但是,如果slave出現網絡阻塞,導緻master的offset遠遠超過了slave的offset:

redis分布式叢集

如果master繼續寫入新資料,其offset就會覆寫舊的資料,直到将slave現在的offset也覆寫:

redis分布式叢集

棕色框中的紅色部分,就是尚未同步,但是卻已經被覆寫的資料。此時如果slave恢複,需要同步,卻發現自己的offset都沒有了,無法完成增量同步了。隻能做全量同步。

redis分布式叢集

2.7.主從同步優化

主從同步可以保證主從資料的一緻性,非常重要。

可以從以下幾個方面來優化Redis主從就叢集:

  • 在master中配置repl-diskless-sync yes啟用無磁盤複制,避免全量同步時的磁盤IO。
  • Redis單節點上的記憶體占用不要太大,減少RDB導緻的過多磁盤IO
  • 适當提高repl_baklog的大小,發現slave當機時盡快實作故障恢複,盡可能避免全量同步
  • 限制一個master上的slave節點數量,如果實在是太多slave,則可以采用主-從-從鍊式結構,減少master壓力

主從從架構圖:

redis分布式叢集

2.8.小結

簡述全量同步和增量同步差別?

  • 全量同步:master将完整記憶體資料生成RDB,發送RDB到slave。後續指令則記錄在repl_baklog,逐個發送給slave。
  • 增量同步:slave送出自己的offset到master,master擷取repl_baklog中從offset之後的指令給slave

什麼時候執行全量同步?

  • slave節點第一次連接配接master節點時
  • slave節點斷開時間太久,repl_baklog中的offset已經被覆寫時

什麼時候執行增量同步?

  • slave節點斷開又恢複,并且在repl_baklog中能找到offset時

三、搭建哨兵叢集

3.1.哨兵原理

3.1.1.叢集結構和作用

這裡我們搭建一個三節點形成的Sentinel叢集,來監管之前的Redis主從叢集。如圖:

redis分布式叢集

哨兵的作用如下:

  • 監控:Sentinel 會不斷檢查您的master和slave是否按預期工作
  • 自動故障恢複:如果master故障,Sentinel會将一個slave提升為master。當故障執行個體恢複後也以新的master為主
  • 通知:Sentinel充當Redis用戶端的服務發現來源,當叢集發生故障轉移時,會将最新資訊推送給Redis的用戶端
3.1.2.叢集監控原理

Sentinel基于心跳機制監測服務狀态,每隔1秒向叢集的每個執行個體發送ping指令:

•主觀下線:如果某sentinel節點發現某執行個體未在規定時間響應,則認為該執行個體主觀下線。

•客觀下線:若超過指定數量(quorum)的sentinel都認為該執行個體主觀下線,則該執行個體客觀下線。quorum值最好超過Sentinel執行個體數量的一半。

redis分布式叢集
3.1.3.叢集故障恢複原理

一旦發現master故障,sentinel需要在salve中選擇一個作為新的master,選擇依據是這樣的:

  • 首先會判斷slave節點與master節點斷開時間長短,如果超過指定值(down-after-milliseconds * 10)則會排除該slave節點
  • 然後判斷slave節點的slave-priority值,越小優先級越高,如果是0則永不參與選舉
  • 如果slave-prority一樣,則判斷slave節點的offset值,越大說明資料越新,優先級越高
  • 最後是判斷slave節點的運作id大小,越小優先級越高。

當選出一個新的master後,該如何實作切換呢?

流程如下:

  • sentinel給備選的slave1節點發送slaveof no one指令,讓該節點成為master
  • sentinel給所有其它slave發送slaveof 192.168.150.101 7002 指令,讓這些slave成為新master的從節點,開始從新的master上同步資料。
  • 最後,sentinel将故障節點标記為slave,當故障節點恢複後會自動成為新的master的slave節點
redis分布式叢集
3.1.4.小結

Sentinel的三個作用是什麼?

  • 監控
  • 故障轉移
  • 通知

Sentinel如何判斷一個redis執行個體是否健康?

  • 每隔1秒發送一次ping指令,如果超過一定時間沒有相向則認為是主觀下線
  • 如果大多數sentinel都認為執行個體主觀下線,則判定服務下線

故障轉移步驟有哪些?

  • 首先標明一個slave作為新的master,執行slaveof no one
  • 然後讓所有節點都執行slaveof 新master
  • 修改故障節點配置,添加slaveof 新master

3.2.準備執行個體和配置

要在同一台虛拟機開啟3個執行個體,必須準備三份不同的配置檔案和目錄,配置檔案所在目錄也就是工作目錄。

三個sentinel執行個體資訊如下:

節點 IP PORT
s1 192.168.150.101 27001
s2 192.168.150.101 27002
s3 192.168.150.101 27003

我們建立三個檔案夾,名字分别叫s1、s2、s3:

# 進入/tmp目錄
cd /tmp
# 建立目錄
mkdir s1 s2 s3
           

然後我們在s1目錄建立一個sentinel.conf檔案,添加下面的内容:

port 27001
sentinel announce-ip 192.168.150.101
sentinel monitor mymaster 192.168.150.101 7001 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
dir "/tmp/s1"
           

解讀:

  • port 27001

    :是目前sentinel執行個體的端口
  • sentinel monitor mymaster 192.168.150.101 7001 2

    :指定主節點資訊
    • mymaster

      :主節點名稱,自定義,任意寫
    • 192.168.150.101 7001

      :主節點的ip和端口
    • 2

      :選舉master時的quorum值
  • down-after-milliseconds mymaster

    :在指定的毫秒數内,若主節點沒有應答哨兵的 PING 指令,此時哨兵認為伺服器主觀下線,預設時間為 30 秒。
  • failover-timeout mymaster

    :在該時間内未完成failover(故障轉移),則failover(故障轉移)失敗

然後将s1/sentinel.conf檔案拷貝到s2、s3兩個目錄中(在/tmp目錄執行下列指令):

# 方式一:逐個拷貝
cp s1/sentinel.conf s2
cp s1/sentinel.conf s3
# 方式二:管道組合指令,一鍵拷貝
echo s2 s3 | xargs -t -n 1 cp s1/sentinel.conf
           

修改s2、s3兩個檔案夾内的配置檔案,将端口分别修改為27002、27003:

sed -i -e 's/27001/27002/g' -e 's/s1/s2/g' s2/sentinel.conf
sed -i -e 's/27001/27003/g' -e 's/s1/s3/g' s3/sentinel.conf
           

3.3.啟動

為了友善檢視日志,我們打開3個ssh視窗,分别啟動3個redis執行個體,啟動指令:

# 第1個
redis-sentinel s1/sentinel.conf
# 第2個
redis-sentinel s2/sentinel.conf
# 第3個
redis-sentinel s3/sentinel.conf
           

啟動後:

redis分布式叢集

3.4.測試

嘗試讓master節點7001當機,檢視sentinel日志:

redis分布式叢集

檢視7003,可以看到已經變為主節點了

redis分布式叢集

再啟動7001,哨兵會讓7001成為7003的從節點

問題:讓7001當機後,哨兵沒有進行故障轉移

redis分布式叢集

解決方案

  • 兩個從機的redis.conf 檔案沒有配置這個
  • redis分布式叢集

3.5.RedisTemplate

在Sentinel叢集監管下的Redis主從叢集,其節點會因為自動故障轉移而發生變化,Redis的用戶端必須感覺這種變化,及時更新連接配接資訊。Spring的RedisTemplate底層利用lettuce實作了節點的感覺和自動切換。

3.5.1.引入依賴

在項目的pom檔案中引入依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
           
3.5.2.配置Redis位址

然後在配置檔案application.yml中指定redis的sentinel相關資訊:

spring:
  redis:
    sentinel:
      master: mymaster
      nodes:
        - 192.168.150.101:27001
        - 192.168.150.101:27002
        - 192.168.150.101:27003
           
3.5.3.配置讀寫分離

在項目的啟動類中,添加一個新的bean:

@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
    return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
           

這個bean中配置的就是讀寫政策,包括四種:

  • MASTER:從主節點讀取
  • MASTER_PREFERRED:優先從master節點讀取,master不可用才讀取replica
  • REPLICA:從slave(replica)節點讀取
  • REPLICA _PREFERRED:優先從slave(replica)節點讀取,所有的slave都不可用才讀取master
3.5.4.測試

啟動項目,浏覽器通路:

http://localhost:8080/get/num

redis分布式叢集

檢視日志

redis分布式叢集

把日志清掉,重新整理界面,檢視結果

redis分布式叢集

可以看到請讀求走的7003

浏覽器發送:

http://localhost:8080/set/num/111

redis分布式叢集
redis分布式叢集

看到寫走的7001,符合讀寫分離

四、Redis分片叢集

4.1.搭建分片叢集

主從和哨兵可以解決高可用、高并發讀的問題。但是依然有兩個問題沒有解決:

  • 海量資料存儲問題
  • 高并發寫的問題

使用分片叢集可以解決上述問題,如圖:

redis分布式叢集

分片叢集特征:

  • 叢集中有多個master,每個master儲存不同資料
  • 每個master都可以有多個slave節點
  • master之間通過ping監測彼此健康狀态
  • 用戶端請求可以通路叢集任意節點,最終都會被轉發到正确節點
4.1.1.準備執行個體和配置

這裡我們會在同一台虛拟機中開啟6個redis執行個體,模拟分片叢集,資訊如下:

IP PORT 角色
192.168.150.101 7001 master
192.168.150.101 7002 master
192.168.150.101 7003 master
192.168.150.101 8001 slave
192.168.150.101 8002 slave
192.168.150.101 8003 slave

删除之前的7001、7002、7003這幾個目錄,重新建立出7001、7002、7003、8001、8002、8003目錄:

# 進入/tmp目錄
cd /tmp
# 删除舊的,避免配置幹擾
rm -rf 7001 7002 7003
# 建立目錄
mkdir 7001 7002 7003 8001 8002 8003
           

在/tmp下準備一個新的redis.conf檔案,内容如下:

port 6379
# 開啟叢集功能
cluster-enabled yes
# 叢集的配置檔案名稱,不需要我們建立,由redis自己維護
cluster-config-file /tmp/6379/nodes.conf
# 節點心跳失敗的逾時時間
cluster-node-timeout 5000
# 持久化檔案存放目錄
dir /tmp/6379
# 綁定位址
bind 0.0.0.0
# 讓redis背景運作
daemonize yes
# 注冊的執行個體ip
replica-announce-ip 192.168.150.101
# 保護模式
protected-mode no
# 資料庫數量
databases 1
# 日志
logfile /tmp/6379/run.log
           

将這個檔案拷貝到每個目錄下:

# 進入/tmp目錄
cd /tmp
# 執行拷貝
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf
           

修改每個目錄下的redis.conf,将其中的6379修改為與所在目錄一緻:

# 進入/tmp目錄
cd /tmp
# 修改配置檔案
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf
           
4.1.2.啟動

因為已經配置了背景啟動模式,是以可以直接啟動服務:

# 進入/tmp目錄
cd /tmp
# 一鍵啟動所有服務
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf
           

通過ps檢視狀态:

ps -ef | grep redis
           

如果要關閉所有程序,可以執行指令:

或者(推薦這種方式):

4.1.3.建立叢集

雖然服務啟動了,但是目前每個服務之間都是獨立的,沒有任何關聯。

我們需要執行指令來建立叢集,在Redis5.0之前建立叢集比較麻煩,5.0之後叢集管理指令都內建到了redis-cli中。

1)Redis5.0之前

Redis5.0之前叢集指令都是用redis安裝包下的src/redis-trib.rb來實作的。因為redis-trib.rb是有ruby語言編寫的是以需要安裝ruby環境。

# 安裝依賴
yum -y install zlib ruby rubygems
gem install redis
           

然後通過指令來管理叢集:

# 進入redis的src目錄
cd /tmp/redis-6.2.4/src
# 建立叢集
./redis-trib.rb create --replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003
           

2)Redis5.0以後

我們使用的是Redis6.2.4版本,叢集管理以及內建到了redis-cli中,格式如下:

redis-cli --cluster create --cluster-replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003
           

指令說明:

  • redis-cli --cluster

    或者

    ./redis-trib.rb

    :代表叢集操作指令
  • create

    :代表是建立叢集
  • --replicas 1

    或者

    --cluster-replicas 1

    :指定叢集中每個master的副本個數為1,此時

    節點總數 ÷ (replicas + 1)

    得到的就是master的數量。是以節點清單中的前n個就是master,其它節點都是slave節點,随機配置設定到不同master

運作後的樣子:

redis分布式叢集

這裡輸入yes,則叢集開始建立:

redis分布式叢集

通過指令可以檢視叢集狀态:

redis-cli -p 7001 cluster nodes
           
redis分布式叢集

建立叢集時如果出現以下問題:

redis分布式叢集

去安全組開放端口,我這裡用的是7001,7002,7003,8001,8002,8003,安全組不僅要開放這6個端口,還要開放17001,17002,17003,18001,18002,18003

原因:

每個Redis叢集中的節點都需要打開兩個TCP連接配接。一個連接配接用于正常的給Client提供服務,比如6379,還有一個額外的端口(通過在這個端口号上加10000)作為資料端口,比如16379。第二個端口(本例中就是16379)用于叢集總線,這是一個用二進制協定的點對點通信信道。這個叢集總線(Cluster bus)用于節點的失敗偵測、配置更新、故障轉移授權,等等。用戶端從來都不應該嘗試和這些叢集總線端口通信,它們隻應該和正常的Redis指令端口進行通信。注意,確定在你的防火牆中開放着兩個端口,否則,Redis叢集節點之間将無法通信。

指令端口和叢集總線端口的偏移量總是10000

redis分布式叢集

叢集操作時,需要給

redis-cli

加上

-c

參數才可以:

4.2.散列插槽

4.2.1.插槽原理

Redis會把每一個master節點映射到0~16383共16384個插槽(hash slot)上,檢視叢集資訊時就能看到:

redis分布式叢集

資料key不是與節點綁定,而是與插槽綁定。redis會根據key的有效部分計算插槽值,分兩種情況:

  • key中包含"{}",且“{}”中至少包含1個字元,“{}”中的部分是有效部分
  • key中不包含“{}”,整個key都是有效部分

例如:key是num,那麼就根據num計算,如果是{itcast}num,則根據itcast計算。計算方式是利用CRC16算法得到一個hash值,然後對16384取餘,得到的結果就是slot值。

redis分布式叢集

如圖,在7001這個節點執行set a 1時,對a做hash運算,對16384取餘,得到的結果是15495,是以要存儲到103節點。

到了7003後,執行

get num

時,對num做hash運算,對16384取餘,得到的結果是2765,是以需要切換到7001節點

4.2.2.小結

Redis如何判斷某個key應該在哪個執行個體?

  • 将16384個插槽配置設定到不同的執行個體
  • 根據key的有效部分計算哈希值,對16384取餘
  • 餘數作為插槽,尋找插槽所在執行個體即可

如何将同一類資料固定的儲存在同一個Redis執行個體?

  • 這一類資料使用相同的有效部分,例如key都以{typeId}為字首

4.3.叢集伸縮

redis-cli --cluster提供了很多操作叢集的指令,可以通過下面方式檢視:

4.3.1.需求分析

需求:向叢集中添加一個新的master節點,并向其中存儲 num = 10

  • 啟動一個新的redis執行個體,端口為7004
  • 添加7004到之前的叢集,并作為一個master節點
  • 給7004節點配置設定插槽,使得num這個key可以存儲到7004執行個體

這裡需要兩個新的功能:

  • 添加一個節點到叢集中
  • 将部分插槽配置設定到新插槽
4.3.2.建立新的redis執行個體

建立一個檔案夾:

拷貝配置檔案:

修改配置檔案:

printf '%s\n' 7004 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf
           

安全組開啟7004和17004端口後啟動

redis-server 7004/redis.conf
           
4.3.3.添加新節點到redis

添加節點的文法如下:

redis分布式叢集

執行指令:

redis-cli --cluster add-node  192.168.150.101:7004 192.168.150.101:7001
           

通過指令檢視叢集狀态:

redis-cli -p 7001 cluster nodes
           

如圖,7004加入了叢集,并且預設是一個master節點:

redis分布式叢集

但是,可以看到7004節點的插槽數量為0,是以沒有任何資料可以存儲到7004上

4.3.4.轉移插槽

我們要将num存儲到7004節點,是以需要先看看num的插槽是多少:num的插槽為2765.

redis分布式叢集

我們可以将0~3000的插槽從7001轉移到7004,指令格式如下:

redis分布式叢集

具體指令如下:

建立連接配接:

redis-cli --cluster reshard 192.168.150.101:7001
           

得到下面的回報:

redis分布式叢集

2765<3000,轉移3000個就行

redis分布式叢集

source1:填7001的id

這裡詢問,你的插槽是從哪裡移動過來的?

  • all:代表全部,也就是三個節點各轉移一部分
  • 具體的id:目标節點的id
  • done:沒有了
redis分布式叢集

通過指令檢視結果:

redis-cli -p 7001 cluster nodes
           
redis分布式叢集

4.4.故障轉移

叢集現在狀态是這樣的:

redis分布式叢集

其中7001、7002、7003、7004都是master,我們計劃讓7002當機。

4.4.1.自動故障轉移

當叢集中有一個master當機會發生什麼呢?

直接停止一個redis執行個體,例如7002:

1)首先是該執行個體與其它執行個體失去連接配接

2)然後是疑似當機

3)最後是确定下線,自動提升一個slave為新的master:

redis分布式叢集

4)當7002再次啟動,就會變為一個slave節點了:

redis-server 7002/redis.conf
           
redis分布式叢集
4.4.2.手動故障轉移

利用cluster failover指令可以手動讓叢集中的某個master當機,切換到執行cluster failover指令的這個slave節點,實作無感覺的資料遷移。其流程如下:

redis分布式叢集

這種failover指令可以指定三種模式:

  • 預設:預設的流程,如圖1~6歩
  • force:省略了對offset的一緻性校驗
  • takeover:直接執行第5歩,忽略資料一緻性、忽略master狀态和其它master的意見

案例需求:在7002這個slave節點執行手動故障轉移,重新奪回master地位

步驟如下:

1)利用redis-cli連接配接7002這個節點

2)執行cluster failover指令

如圖:

redis分布式叢集

效果:

redis分布式叢集

4.5.RedisTemplate通路分片叢集

RedisTemplate底層同樣基于lettuce實作了分片叢集的支援,而使用的步驟與哨兵模式基本一緻:

1)引入redis的starter依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
           

2)配置分片叢集位址

3)配置讀寫分離

與哨兵模式相比,其中隻有分片叢集的配置方式略有差異,如下:

spring:
  redis:
    cluster:
      nodes:
        - 150.158.164.135:7001
        - 150.158.164.135:7002
        - 150.158.164.135:7003
        - 150.158.164.135:7004
        - 150.158.164.135:8001
        - 150.158.164.135:8002
        - 150.158.164.135:8003
    lettuce:
      pool:
        max-active: 20 # 連接配接池最大連接配接數(使用負值表示沒有限制)
        max-idle: 10 # 連接配接池中的最大空閑連接配接
        min-idle: 5 # 連接配接池中的最小空閑連接配接
        max-wait: 5000ms # 連接配接池最大阻塞等待時間(使用負值表示沒有限制)
           

在浏覽器通路:

http://localhost:8080/get/num

redis分布式叢集

因為上面做插槽轉移num應該打到7004上,7004是主節點,沒有從節點,是以打到了7004上

redis分布式叢集

通路個get/b,8001是從節點,讀通路從節點

redis分布式叢集
redis分布式叢集

set/b/qqq,應該寫7001

redis分布式叢集
redis分布式叢集

繼續閱讀