Redis叢集與高可用

leikj發表於2024-07-19

雖然Redis可以實現單機的資料持久化,但無論是RDB也好或者AOF也好,都解決不了單點當機問題,即一旦單臺 redis伺服器本身出現系統故障、硬體故障等問題後,就會直接造成資料的丟失。此外,單機的效能也是有極限的,因此需要使用另外的技術來解決單點故障和效能擴充套件的問題

一、Redis主從複製

主從模式(master/slave),可以實現Redis資料的跨主機備份。

主從複製是高可用Redis的基礎,哨兵和叢集都是在主從複製基礎上實現高可用的。主從複製主要實現了資料的多機備份,以及對於讀操作的負載均衡和簡單的故障恢復。缺陷:故障恢復無法自動化;寫操作無法負載均衡;儲存能力收到單機的限制。

主從複製特點

  • 一個master可以有多個slave

  • 一個slave只能有一個master

  • 資料流向是從master到slave單向的

1.主從複製實現

當配置Rdeis複製功能時,強烈建議開啟主服務的持久化功能。否則的話,由於延遲等問題,部署的主節點Redis服務應該要避免自動啟動。

Redis Slave也要開啟持久化並設定和master同樣的;連線密碼,因為後期Slave會有提升為master的可能,slave端切換master同步後會丟失之前的所有資料,而透過持久化可以恢復資料。

一旦某個slave成為一個master的slave,Redis Slave服務會清空當前redis伺服器上的所有資料並將master的資料匯入到自己的記憶體,但是如果只是斷開同步關係後,則不會刪除當前已經同步過的資料。

實際操作:

  • master 192.168.204.60
  • slave1 192.168.204.50
  • slave2 192.168.204.70

主伺服器master:

點選檢視程式碼
[root@node6 ~]#  systemctl stop firewalld
[root@node6 ~]#  setenforce 0
安裝redis

[root@node6 ~]#  vim /apps/redis/etc/redis.conf  //修改配置檔案
 70 bind 0.0.0.0                             //將監聽埠改為任意埠
 172 logfile /apps/redis/log/redis6379.log   //指定日誌檔案目錄
 264 dir /apps/redis/data/                   //指定工作目錄
 700 appendonly yes                          //開啟AOF持久化功能
[root@node6 ~]#  systemctl restart redis         //重啟服務

從伺服器slave1:

點選檢視程式碼
[root@node5 ~]#  systemctl stop firewalld
[root@node5 ~]#  setenforce 0
安裝redis

[root@node5 ~]#  vim /apps/redis/etc/redis.conf
 70 bind 0.0.0.0
 172 logfile /apps/redis/log/redis6379.log
 264 dir /apps/redis/data/
 288  replicaof 192.168.204.60 6379  //設定 主從配置
 700 appendonly yes
[root@node5 ~]#  systemctl restart redis

從伺服器slave2:

點選檢視程式碼
[root@node7 ~]#  systemctl stop firewalld
[root@node7 ~]#  setenforce 0
安裝redis

[root@node7 ~]#  vim /apps/redis/etc/redis.conf
 70 bind 0.0.0.0
 172 logfile /apps/redis/log/redis6379.log
 264 dir /apps/redis/data/
 288  replicaof 192.168.204.60 6379  //設定 主從配置
 700 appendonly yes
[root@node7 ~]#  systemctl restart redis

2.實現redis的級聯複製

master和slave1節點無需修改,只需要修改slave2指向slave1做為mater即可。

  • master 192.168.204.60
  • slave1 192.168.204.50
  • slave2 192.168.204.70

主伺服器master:

點選檢視程式碼
[root@node6 ~]#  systemctl stop firewalld
[root@node6 ~]#  setenforce 0
安裝redis

[root@node6 ~]#  vim /apps/redis/etc/redis.conf  //修改配置檔案
 70 bind 0.0.0.0                             //將監聽埠改為任意埠
 172 logfile /apps/redis/log/redis6379.log   //指定日誌檔案目錄
 264 dir /apps/redis/data/                   //指定工作目錄
 700 appendonly yes                          //開啟AOF持久化功能
[root@node6 ~]#  systemctl restart redis         //重啟服務

從伺服器slave1:

點選檢視程式碼
[root@node5 ~]#  systemctl stop firewalld
[root@node5 ~]#  setenforce 0
安裝redis

[root@node5 ~]#  vim /apps/redis/etc/redis.conf
 70 bind 0.0.0.0
 172 logfile /apps/redis/log/redis6379.log
 264 dir /apps/redis/data/
 288  replicaof 192.168.204.60 6379  //設定 主從配置
 700 appendonly yes
[root@node5 ~]#  systemctl restart redis

從伺服器slave2:

點選檢視程式碼
[root@node7 ~]#  systemctl stop firewalld
[root@node7 ~]#  setenforce 0
安裝redis

[root@node7 ~]#  vim /apps/redis/etc/redis.conf
 70 bind 0.0.0.0
 172 logfile /apps/redis/log/redis6379.log
 264 dir /apps/redis/data/
 288  replicaof 192.168.204.50 6379  //設定 主從配置
 700 appendonly yes
[root@node7 ~]#  systemctl restart redis

3.主從複製過程

Redis主從複製分為全量同步和增量同步。

點選檢視程式碼
`具體主從同步過程如下:`
1)從伺服器連線主伺服器,傳送PSYN(同步)C命令
2)主伺服器接收到PSYNC命令後,開始執行BGSAVE命令生成RDB快照檔案並使用緩衝區記錄此後執行的所有寫命令
3)主伺服器BGSAVE執行完後,向所有從伺服器傳送RDB快照檔案,並在傳送期間繼續記錄被執行的寫命令
4)從伺服器收到快照檔案後丟棄所有舊資料,載入收到的快照至記憶體
5)主伺服器快照傳送完畢後,開始向從伺服器傳送緩衝區中的寫命令
6)從伺服器完成對快照的載入,開始接收命令請求,並執行來自主伺服器緩衝區的寫命令
7)後期同步會先傳送自己slave_repl_offset位置,只同步新增加的資料,不再全量同步

4.主從複製最佳化

複製緩衝區(環形佇列)配置引數

點選檢視程式碼
//複製緩衝區大小,建議要設定足夠大
repl-backlog-size 1mb 

//Redis同時也提供了當沒有slave需要同步的時候,多久可以釋放環形佇列:  
repl-backlog-ttl   3600   //最長保持時間  3600秒

避免全量複製

  • 第一次全量複製不可避免,後續的全量複製可以利用小主節點(記憶體小),業務低峰時進行全量

  • 節點執行ID不匹配:主節點重啟會導致RUNID變化,可能會觸發全量複製,可以利用故障轉移,例如哨兵或叢集,而從節點重啟動,不會導致全量複製

  • 複製積壓緩衝區不足: 當主節點生成的新資料大於緩衝區大小,從節點恢復和主節點連線後,會導致全量複製。解決方法將 repl-backlog-size 調大。

避免複製風暴

單主節點複製風暴

  • 當主節點重啟,多從節點複製。解決方法:更換複製拓撲

單機器多例項複製風暴

  • 機器當機後,大量全量複製。解決方法:主節點分散多機器

效能相關配置

點選檢視程式碼
repl-diskless-sync no 
//是否使用無盤同步RDB檔案,預設為no,no為不使用無盤,需要將RDB檔案儲存到磁碟後再傳送給slave,yes為支援無盤,支援無盤就是RDB檔案不需要儲存至本地磁碟,而且直接透過socket檔案傳送給slave

repl-diskless-sync-delay 5 
//diskless時複製的伺服器等待的延遲時間

repl-ping-slave-period 10 
//slave端向server端傳送ping的時間間隔,預設為10秒

repl-timeout 60 
//設定主從ping連線超時時間,超過此值無法連線,master_link_status顯示為down,並記錄錯誤日誌

repl-disable-tcp-nodelay no 
//是否啟用TCP_NODELAY,如設定成yes,則redis會合並小的TCP包從而節省頻寬, 但會增加同步延遲(40ms),造成master與slave資料不一致,假如設定成no,則redis master會立即傳送同步資料,沒有延遲,yes關注網路效能,no關注redis服務中的資料一致性

repl-backlog-size 1mb 
//master的寫入資料緩衝區,用於記錄自上一次同步後到下一次同步過程中間的寫入命令,計算公式:repl-backlog-size = 允許從節點最大中斷時長 * 主例項offset每秒寫入量,比如master每秒最大寫入64mb,最大允許60秒,那麼就要設定為64mb*60秒=3840MB(3.8G),建議此值是設定的足夠大

repl-backlog-ttl 3600 
//3600秒   如果一段時間後沒有slave連線到master,則backlog size的記憶體將會被釋放。如果值為0則 表示永遠不釋放這部份記憶體。

slave-priority 100 
//slave端的優先順序設定,值是一個整數,數字越小表示優先順序越高。當master故障時將會按照優先順序來選擇slave端進行恢復,如果值設定為0,則表示該slave永遠不會被選擇。

min-replicas-to-write 1 
//設定一個master的可用slave不能少於多少個,否則master無法執行寫

min-slaves-max-lag 20 
//設定至少有上面數量的slave延遲時間都大於多少秒時,master不接收寫操作(拒絕寫入)

5.常見主從複製故障

1.master密碼不對

即配置的master密碼不對,導致驗證不透過而無法建立主從同步關係。

2.Redis版本不一致

不同的redis 大版本之間存在相容性問題,比如:3和4,4和5之間,因此各master和slave之間必須保持版本一致。

3.無法遠端連線

在開啟了安全模式情況下,沒有設定bind地址或者密碼。

4.配置不一致

主從節點的maxmemory不一致,主節點記憶體大於從節點記憶體,主從複製可能丟失資料rename-command 命令不一致,如在主節點定義了fushall,flushdb,從節點沒定義,結果執行flushdb,不同步。

二、哨兵模式(Sentinel)

Sentinel實際上是一個特殊的redis伺服器,有些redis指令支援,但很多指令並不支援。預設監聽在26379/tcp 埠。

1.哨兵工作原理

sentinel中的三個定時任務:

  • 每10秒每個sentinel對master和slave執行info

發現slave節點
確認主從關係

  • 每2秒每個sentinel透過master節點的channel交換資訊(pub/sub)

透過sentinel__:hello頻道互動
互動對節點的“看法”和自身資訊

  • 每1秒每個sentinel對其他sentinel和redis執行ping

2.實現哨兵

哨兵的前提是已經實現了一個redis的主從複製的執行環境,從而實現一個一主兩從基於哨兵的高可用

  • master 192.168.204.60
  • slave 192.168.204.50
  • slave 192.168.204.70

1.先實現主從複製 (見一、1.主從複製實現

點選檢視程式碼
`在所有主從節點執行`
vim /etc/redis.conf
bind 0.0.0.0
masterauth "123456"    //設定 Redis 主節點的密碼
requirepass "123456"   //設定 Redis 從節點的密碼

systemctl start redis  //注意!先開啟主節點redis!再開啟從

redis-cli -a 123456    //由於上面在配置檔案中新增了密碼,所有此處需要-a輸入密碼登入
127.0.0.1:6379> info replication  //檢視主從狀態

2.編輯哨兵的配置檔案

點選檢視程式碼
[root@node6 ~]#  cd /data/redis-5.0.7/
[root@node6 redis-5.0.7]#  ls
00-RELEASENOTES  CONTRIBUTING  deps     Makefile   README.md   runtest          runtest-moduleapi  sentinel.conf  tests
BUGS             COPYING       INSTALL  MANIFESTO  redis.conf  runtest-cluster  runtest-sentinel   src            utils
[root@node6 redis-5.0.7]#  cp sentinel.conf /apps/redis/etc/
[root@node6 redis-5.0.7]#  cd /apps/redis/etc/
[root@node6 etc]#  ls
redis.conf  sentinel.conf
[root@node6 etc]#  chown -R redis.redis /apps/redis/etc/

[root@node6 etc]#  vim sentinel.conf  //編輯配置檔案
 16 bind 0.0.0.0   //修改監聽埠
 22 port 26379     //預設監聽埠,不用修改
 27 daemonize no   //不用修改
 32 pidfile /apps/redis/run/redis-sentinel.pid  //指定pid檔案位置
 37 logfile /apps/redis/log/sentinel.log        //指定日誌檔案位置  
 66 dir /tmp       //工作目錄不用修改
 85 sentinel monitor mymaster 192.168.204.60 6379 2
//mymaster是叢集的名稱,此行指定當前mymaster叢集中master伺服器的地址和埠。2為法定人數限制(quorum),即有幾個sentinel認為master down了就進行故障轉移
 88 sentinel auth-pass mymaster 123456  
//mymaster叢集中master的密碼
115 sentinel down-after-milliseconds mymaster 3000 
//(SDOWN)判斷mymaster叢集中所有節點的主觀下線的時間。單位:毫秒
123 sentinel parallel-syncs mymaster 1
//發生故障轉移後,可以同時向新master同步資料的slave的數量,數字越小總同步時間越長,但可以減輕新master的負載壓力
148 sentinel failover-timeout mymaster 1800
//所有slaves指向新的master所需的超時時間,單位:毫秒


[root@node6 etc]#  scp sentinel.conf 192.168.204.50:/apps/redis/etc/  //複製配置檔案
[root@node6 etc]#  scp sentinel.conf 192.168.204.70:/apps/redis/etc/

[root@node5 ~]# cd /apps/redis/etc/
[root@node5 etc]#  chown redis.redis sentinel.conf  
[root@node7 ~]# cd /apps/redis/etc/
[root@node7 etc]#  chown redis.redis sentinel.conf

3.準備service檔案,全部節點都需要

點選檢視程式碼
[root@node6 etc]#  cat  >> /lib/systemd/system/redis-sentinel.service  <<eof
> [Unit]
> Description=Redis Sentinel
> After=network.target
> [Service]
> ExecStart=/apps/redis/bin/redis-sentinel /apps/redis/etc/sentinel.conf --supervised systemd
> ExecStop=/bin/kill -s QUIT $MAINPID
> User=redis
> Group=redis
> RuntimeDirectory=redis
> RuntimeDirectoryMode=0755
> [Install]
> WantedBy=multi-user.target
> eof
[root@node6 etc]#  systemctl daemon-reload
[root@node6 etc]#  systemctl start redis-sentinel.service

[root@node6 etc]#  ss -natp |grep 26379

4.關掉主伺服器進行驗證

點選檢視程式碼
[root@node6 ~]#  systemctl stop redis

主在恢復後會變成從

點選檢視程式碼
[root@node6 ~]#  systemctl restart redis

三、Redis Cluster

1.Redis Cluster工作原理

在哨兵sentinel機制中,可以解決redis高可用問題,即當master故障後可以自動將slave提升為master,從而可以保證redis服務的正常使用,但是無法解決redis單機寫入的瓶頸問題,即單機redis寫入效能受限於單機的記憶體大小、併發數量、網路卡速率等因素。
為了解決單機效能的瓶頸,提高Redis 效能,可以使用分散式叢集的解決方案。

Redis Cluster 特點如下:

  • 所有Redis節點使用(PING機制)互聯

  • 叢集中某個節點的是否失效,是由整個叢集中超過半數的節點監測都失效,才能算真正的失效

  • 客戶端不需要proxy即可直接連線redis,應用程式中需要配置有全部的redis伺服器IP

  • redis cluster把所有的redis node 平均對映到 0-16383 槽位 上,讀寫需要到指定的redis node上進行操作,因此有多少個redis node相當於redis 併發擴充套件了多少倍,每個redis node 承擔16384/N個槽位

  • Redis cluster預先分配16384個(slot)槽位,當需要在redis叢集中寫入一個key -value的時候,會使用CRC16(key) mod 16384之後的值,決定將key寫入值哪一個槽位從而決定寫入哪一個Redis節點上,從而有效解決單機瓶頸

2.實現叢集

redis的叢集一般需要6個節點,3主3從。方便起見,開啟多例項,所有節點在同一臺伺服器上模擬。 以埠號進行區分:3個主節點埠號:6001/6002/6003,對應的從節點埠號:6004/6005/6006 (可能隨機搭配)

1.新建叢集檔案目錄

點選檢視程式碼
[root@node5 ~]#  cd  /apps/redis/
[root@node5 redis]#  mkdir -p redis-cluster/redis600{1..6}

2.準備可執行檔案到每個資料夾

點選檢視程式碼
[root@node5 redis]#  for i in {1..6}
> do
> cp /data/redis-5.0.7/redis.conf /apps/redis/redis-cluster/redis600$i
> cp /data/redis-5.0.7/src/redis-cli /data/redis-5.0.7/src/redis-server /apps/redis/redis-cluster/redis600$i
> done

3.開啟叢集功能

點選檢視程式碼
[root@node5 redis]#  cd /apps/redis/redis-cluster/redis6001/
[root@node5 redis6001]#  vim redis.conf
  70 bind 0.0.0.0         //監聽所有網路卡
  89 protected-mode no    //關閉保護模式
  93 port 6001            //修改redis監聽埠
 137 daemonize yes        //開啟守護程序
 701 appendonly yes       //開啟AOF持久化
 834 cluster-enabled yes  //開啟群集功能
 842 cluster-config-file nodes-6001.conf  //群集名稱檔案設定
 848 cluster-node-timeout 15000           //群集超時時間設定

[root@node5 redis6001]#  for i in {2..6}  //複製配置檔案,注意要切換到redis6001目錄下執行
> do
> \cp -f  ./redis.conf   /apps/redis/redis-cluster/redis600${i}
> done

//其他5個資料夾的配置檔案以此類推修改,注意6個埠都要不一樣
[root@node5 redis6001]#  sed  -i   's/6001/6002/'   /apps/redis/redis-cluster/redis6002/redis.conf
[root@node5 redis6001]#  sed  -i   's/6001/6003/'   /apps/redis/redis-cluster/redis6003/redis.conf
[root@node5 redis6001]#  sed  -i   's/6001/6004/'   /apps/redis/redis-cluster/redis6004/redis.conf
[root@node5 redis6001]#  sed  -i   's/6001/6005/'   /apps/redis/redis-cluster/redis6005/redis.conf
[root@node5 redis6001]#  sed  -i   's/6001/6006/'   /apps/redis/redis-cluster/redis6006/redis.conf

[root@node5 redis-cluster]#  chown -R redis.redis /apps/redis/  

4.啟動redis節點

點選檢視程式碼
[root@node5 redis-cluster]#  cd /apps/redis/redis-cluster/redis6001  //切換目錄
[root@node5 redis6001]#  for d in {1..6}   //用指令碼啟動
> do
> cd /apps/redis/redis-cluster/redis600$d
> redis-server redis.conf
> done

5.檢視是否成功啟動

點選檢視程式碼
[root@node5 redis6006]#  ss -natp |grep "\b600[1-6]\b"
LISTEN     0      511          *:6001                     *:*                   users:(("redis-server",pid=59124,fd=6))
LISTEN     0      511          *:6002                     *:*                   users:(("redis-server",pid=59126,fd=6))
LISTEN     0      511          *:6003                     *:*                   users:(("redis-server",pid=59131,fd=6))
LISTEN     0      511          *:6004                     *:*                   users:(("redis-server",pid=59136,fd=6))
LISTEN     0      511          *:6005                     *:*                   users:(("redis-server",pid=59141,fd=6))
LISTEN     0      511          *:6006                     *:*                   users:(("redis-server",pid=59146,fd=6))

6.啟動叢集

點選檢視程式碼
[root@node5 redis6006]# redis-cli --cluster create 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:6004 127.0.0.1:6005 127.0.0.1:6006 --cluster-replicas 1
//六個例項分為三組,每組一主一從,前面的做主節點,後面的做從節點。下面互動的時候 需要輸入yes才可以建立。
//--replicas 1 表示每個主節點有1個從節點

7.測試叢集

點選檢視程式碼
[root@node5 redis6006]#  redis-cli -p 6001  -c  //加-c引數,節點之間就可以互相跳轉
127.0.0.1:6001> cluster slots   //檢視節點的雜湊槽編號範圍
1) 1) (integer) 0
   2) (integer) 5460        //雜湊槽編號範圍
   3) 1) "127.0.0.1"   
      2) (integer) 6001     //主節點IP和埠號
      3) "b4ebafd25670e4bf6f3e9fa16bad9d1a0441d981"
   4) 1) "127.0.0.1"
      2) (integer) 6005     //從節點IP和埠號
      3) "043eac265825debc6efe592a25f4e388b89a3560"
2) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 6003
      3) "a925a46cbe6808a38b4b93cf4d7a86990a405d49"
   4) 1) "127.0.0.1"
      2) (integer) 6004
      3) "1de8699171fd789095fc7eb54de556f6da68cf17"
3) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 6002
      3) "8b332121f676674bd4beb3ef2bfa6cc8220c2873"
   4) 1) "127.0.0.1"
      2) (integer) 6006
      3) "629aefcf46649f419a824f44117ce9ccae658fac"

8.生成資料測試

點選檢視程式碼
127.0.0.1:6001> keys * 
(empty list or set)
127.0.0.1:6001> set name zwy  //對name鍵進行演算法,得出值為5789,跳到對應的節點儲存
-> Redirected to slot [5798] located at 127.0.0.1:6002
OK
127.0.0.1:6002> keys *  //此處可以看到跳轉到6002
1) "name"

9.檢視節點資訊

點選檢視程式碼
127.0.0.1:6002> CLUSTER NODES  //檢視節點資訊,注意大寫
043eac265825debc6efe592a25f4e388b89a3560 127.0.0.1:6005@16005 slave b4ebafd25670e4bf6f3e9fa16bad9d1a0441d981 0 1720616594403 1 connected
b4ebafd25670e4bf6f3e9fa16bad9d1a0441d981 127.0.0.1:6001@16001 master - 0 1720616594000 1 connected 0-5460
629aefcf46649f419a824f44117ce9ccae658fac 127.0.0.1:6006@16006 slave 8b332121f676674bd4beb3ef2bfa6cc8220c2873 0 1720616594000 2 connected
a925a46cbe6808a38b4b93cf4d7a86990a405d49 127.0.0.1:6003@16003 master - 0 1720616594000 3 connected 10923-16383
8b332121f676674bd4beb3ef2bfa6cc8220c2873 127.0.0.1:6002@16002 myself,master - 0 1720616593000 2 connected 5461-10922
1de8699171fd789095fc7eb54de556f6da68cf17 127.0.0.1:6004@16004 slave a925a46cbe6808a38b4b93cf4d7a86990a405d49 0 1720616595410 3 connected

相關文章