面試官:介紹一下 Redis 三種叢集模式

2022-11-28 09:00:22

小碼今天去面試。

面試官:給我介紹一下Redis叢集,

小碼:啊,平時開發用的都是單機Redis,沒怎麼用過叢集了。

面試官:好的,出門右轉不謝。

小碼內心困惑:在小公司業務量也不大,單機的 Redis 完全夠用,也不會發生宕機問題啊。面試要問到 Redis 叢集該怎麼辦呢?

Redis 為何要有叢集

很多小夥伴也有類似的困惑,自己的公司並不大。並行量、存取量要求不高,使用單機 Redis 就能很好的解決資料請求的問題。為啥要使用叢集呢?

對於存取量小、並行低的系統,對資料的高可用要求不高的資料,單機部署都能滿足需求。但是在大公司,隨便一個系統的QPS都是成千上萬,對系統的高可用高並行要求比較高,這個時候就需要要使用Redis叢集模式了,Redis有三種叢集模式:

  • 主從複製模式
  • 哨兵模式
  • Cluster 模式

主從複製模式

Redis想要不丟失資料,就需要開始持久化,資料會寫入到磁碟中,這樣即使服務關閉再重啟伺服器後,資料也能從硬碟載入到記憶體中。但是如果伺服器的硬碟出現故障,也會導致資料丟失。

為了避免硬碟故障問題,需要將資料複製的多個副本上,這樣即使一個服務出現故障,其他伺服器也能提供資料服務。

Redis提供了主從模式,一個主資料庫master繫結多個從資料庫slave

主資料可以讀和寫,從資料庫一般是唯讀,主從庫之間採用讀寫分離,主資料庫資料更新後同步複製給繫結的從資料庫,主從資料庫的資料保持一致:

資料主從同步原理

  • 從資料庫啟動後,連線主資料庫,傳送SYNC命令。
  • 主資料庫接收到SYNC命令後,開始執行BGSAVE命令生成RDB檔案並使用緩衝區記錄後面執行的所有寫命令。
  • 主資料庫執行完BGSAVE命令之後,向所有從資料庫傳送RDB檔案。從資料庫載入RDB檔案。
  • 主資料將記錄的快取區所有的寫命令傳送給從資料庫,從資料庫執行命令。

SYNC每次從服務重啟,都會請求所以的資料。如果服務宕機再重啟還是同步所有的資料,就會造成資源的浪費,所以有了PSYNC命令,PSYNC有完整同步和部分同步,其中完整同步和SYNC一致,而部分同步是根據資料偏移量複製資料。

主從複製服務搭建

Redis單機搭建可以檢視前面寫的的教學

Centos安裝單機Redis

首先建立三個資料夾6380、6381、6382

mkdir 6380
mkdir 6381
mkdir 6382

複製redis.conf到這三個資料夾裡:

cp redis.conf 6380/
cp redis.conf 6381/
cp redis.conf 6382/

設定一主兩從6380為主,6381、6382為從。然後修改redis.conf 檔案:

引數 maser (6380) slave1 (6381) slave2 (6382)
port 6380 6381 6382
requirepass requirepass "xxxx" requirepass "xxxx" requirepass "xxxx"
slaveof slaveof 本機ip 6380 slaveof 本機ip 6380
masterauth masterauth 」xxx「 masterauth 」xxx「
pidfile pidfile /redis_6380.pid pidfile /redis_6381.pid pidfile /redis_6382.pid
logfile logfile "redis_6380.log" logfile "redis_6381.log" logfile "redis_6382.log"

設定了requirepass,就需要設定masterauth,三臺伺服器的密碼需要一致。

啟動伺服器:

[root@instance-3 redis]# bin/redis-server 6380/redis.conf 
[root@instance-3 redis]# bin/redis-server 6381/redis.conf 
[root@instance-3 redis]# bin/redis-server 6382/redis.conf 

然後檢視程序,如果有以下的顯示,說明啟動成功了:

[root@instance-3 redis]# ps -ef |grep redis
root      6652     1  0 16:28 ?        00:00:00 bin/redis-server *:6380
root      6665     1  0 16:28 ?        00:00:00 bin/redis-server *:6381
root      6682     1  0 16:28 ?        00:00:00 bin/redis-server *:6382
root      7188  4291  0 16:30 pts/0    00:00:00 grep --color=auto redis

進入Redis使用者端,使用info replication命令檢視資料庫的資訊。

master 6380:

[root@instance-3 redis]# bin/redis-cli -p 6380

127.0.0.1:6380> auth xxxx
OK
127.0.0.1:6380> info replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=42,lag=0
slave1:ip=127.0.0.1,port=6382,state=online,offset=42,lag=1
master_replid:19ca382e3c05014988002a295078687dae9bb92e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42

role:master表示 6380 是主伺服器,slave0salve1 表示繫結的從伺服器。

slave 6381:

role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:126
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:19ca382e3c05014988002a295078687dae9bb92e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:126
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:126

role:slave 表示 6381 是從伺服器,master_hostmaster_port 表示繫結對應的主伺服器。

slave 6382:

role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:476
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:19ca382e3c05014988002a295078687dae9bb92e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:476
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:476

role:slave 表示 6382 是從伺服器,master_hostmaster_port 表示繫結對應的主伺服器。

主伺服器新增資料,再從從伺服器獲取資料。

6380伺服器新增資料:

127.0.0.1:6380> set name jeremy
OK

6381伺服器獲取資料:

127.0.0.1:6381> get name
"jeremy"

經過以上測試,說明主伺服器的資料,從伺服器也能同步獲取。主從服務都搭建成功

主從模式的優缺點

  • 優點

    • 主從模式搭建多個伺服器,即使單個服務宕機,還能繼續服務。
    • 讀資料壓力分解到多個伺服器下,大大緩解伺服器的壓力。
  • 缺點

    • 主資料庫宕機了,宕機後的資料無法同步到從從資料庫,導致資料庫不一致。
    • 主資料庫宕機了,無法自動生成新的主機,新的讀請求也無法處理。
    • 每個伺服器都儲存相同的資訊,比較浪費記憶體。
    • 因為這些問題,Spring Boot也不支援整合Redis主從模式。

主從模式最大的問題就是無法處理主資料庫宕機問題,也就無法保證Redis的高可用性。這就需要有一個自動的機制處理主資料庫宕機問題,這就延伸出下面的模式 —— 哨兵模式。

哨兵模式

當主資料庫掛了之後,需要手動設定新的主資料庫,其他從資料庫都需要重新設定新的主資料。手動切換的成本比較大,還會導致一段時間的服務不可用。這就需要講上面的手動設定改成自動設定,也就是使用哨兵來設定。

哨兵Redis的高可用解決方案,哨兵監控Redis主伺服器和繫結的從伺服器,如果主伺服器宕機了,自動將某個從伺服器升級為新的伺服器,然後傳送通知給其他從伺服器。

哨兵基本原理

哨兵是一個獨立的程序,和Redis一樣,它也執行一個範例。主要有三個任務:

  • 監控: 週期給所有的主從資料庫傳送PING命令,檢查主從資料庫執行是否正常,在設定down-after-milliseconds毫秒,沒有服務響應,就會標記主觀下線,當其他哨兵也判斷主觀下線,判斷主觀下線的數量達到設定的值後,哨兵之間會進行投票,投票同意後,進行資料庫升級。
  • 自動切換主從資料庫: 當上面的投票同意後,會根據一定的規則選取一個從伺服器升級成主伺服器。更新redis.conf組態檔。
  • 通知:完成主伺服器升級之後,哨兵通過釋出訂閱會把新主資料庫的連線資訊傳送給其他從資料庫,修改對應組態檔的replicaof命令,和新資料庫建立連線,並進行資料複製。

哨兵服務搭建

在上面的主從模式的基礎上新增哨兵,首先從解壓資料夾複製sentinel.confusr/local/redis資料夾中:

cp sentinel.conf /usr/local/redis/

修改sentinel.conf檔案,需要修改的部分:

  • daemonize no 改成 daemonize yes
  • logfile "" 改成 logfile "redis_26379.log"
  • 新增 sentinel monitor <master-name> <ip> <redis-port> <quorum>,設定成 sentinel monitor mymaster 127.0.0.1 6382 1
    • ip 主資料庫IP
    • redis-port 主資料庫埠
    • quorum 主從切換需要達到主動下線個數
  • 如果資料庫有密碼,新增 sentinel auth-pass mymaster 123456,表示驗證密碼
    • mymaster 哨兵的名稱,需要唯一
    • 123456 資料庫密碼,所有主從資料庫密碼需要設定成一致。

啟動伺服器:

[root@instance-3 redis]# bin/redis-sentinel sentinel.conf 

檢視logfile啟動紀錄檔:

看最後標記的三行,表明哨兵分別監控了主資料庫6380、兩個從資料庫63816382。有上面的紀錄檔輸出表明哨兵已經成功啟動。

模擬主從切換

使用SHUTDOWN命令關閉6380主資料庫服務:

[root@instance-3 redis]# bin/redis-cli -p 6380
127.0.0.1:6380> shutdown
(error) NOAUTH Authentication required.
127.0.0.1:6380> a
[root@instance-3 redis]# bin/redis-cli -p 6380
127.0.0.1:6380> auth xxx
OK
127.0.0.1:6380> SHUTDOWN

通過ps -ef |grep redis檢視6380已經關閉:

[root@instance-3 redis]# ps -ef |grep redis
root      8822     1  0 Nov21 ?        00:00:58 /usr/local/redis/bin/redis-server *:6379
root     24707     1  0 10:35 ?        00:00:02 bin/redis-server *:6381
root     27500     1  0 10:47 ?        00:00:01 bin/redis-server *:6382
root     29247     1  0 10:54 ?        00:00:03 bin/redis-sentinel *:26379 [sentinel]
root     34131 17210  0 11:16 pts/1    00:00:00 grep --color=auto redis

檢視哨兵紀錄檔logfile

表明主伺服器從6380成功切換到了6382sentinel.conf組態檔也修改了主從資料庫設定。如果沒有切換成功,紀錄檔報錯-failover-abort-no-good-slave,可能是沒有設定驗證密碼sentinel auth-pass

哨兵模式的優缺點

  • 優點
    • 哨兵模式是基於主從模式,主從模式的優點,哨兵模式都有。
    • 哨兵模式使用獨立程序監控服務,自動切換宕機資料庫,保障服務的高可用。
  • 缺點
    • 受限於單個伺服器,很難實現單節點線上擴容。
    • 每個伺服器都儲存相同的資訊,比較浪費記憶體。

Cluster(官方推薦)叢集

主從模式和哨兵模式資料庫都儲存了相同的資料,比較浪費記憶體。而且當資料量增加時,在單個資料庫上很難實現線上擴容。Redis Cluster將資料分佈儲存在不同的節點上,每個節點儲存不同的資料。新增節點就能解決擴容問題。

Redis Cluster翻譯就是Redis叢集,Redis叢集提供分散式解決方案,通過分片將資料拆分到不同的節點上,並提供複製和故障轉移功能。使用了水平擴充套件的方法,將資料分發到不同的資料庫中。

每個虛線圓都表示一個節點,每個節點都有一個主資料庫和多個從資料庫。任意兩個節點都是相同的(三個節點畫圖容易誤以為是一個環,四個節點容易理解),節點之間都共用資料。

分片叢集原理

Redis分片叢集,使用了一種類似於一致性雜湊的分片技術——雜湊槽,每個鍵都有一個雜湊槽的值,Redis 叢集有16384個雜湊嘈,對鍵的CRC16取模16384計算出雜湊槽,以此決定放置的雜湊嘈的位置。

Redis叢集中每個節點都負責一部分雜湊嘈,比如,叢集有三個節點,其中:

  • 節點 A 包含 0 到 5460 號雜湊槽
  • 節點 B 包含 5461 到 10922 號雜湊槽
  • 節點 C 包含 10923 到 16383 號雜湊槽

資料根據雜湊嘈分配到不同的資料庫中,實現資料的分片。這裡新增或者減少一個節點就比較容易了。比如,我想新增一個新的節點D,需要將節點A、B、C一部分資料移動到節點D中。而刪除一個節點A,就將原來A節點的資料分發到其它節點上。

Redis叢集主從模式

為了保證高可用,Redis Cluster也使用了主從模式。節點(上圖虛線圓)宕機了,就無法提供繼續資料服務了。當節點引入主從模式後,主服務宕機之後,從伺服器升級成主服務。但是如果一個節點的所有主從服務服務都宕機了,該節點就無法提供資料服務了。

叢集模式搭建

最小叢集必須最少包含三個節點,這裡部署使用三個主節點,三個從節點。一共有六個組態檔,埠分別是7001、7002、7003、7004、7005、7006

複製redis.conf組態檔命名redis_7001.conf,修改以下欄位:

# 埠
port 7001
# 啟用叢集模式
cluster-enabled yes
# 儲存其他節點的名稱、狀態等資訊,命名和埠保持一致
cluster-config-file nodes_7001.conf
logfile "redis_7001.log"
daemonize yes
protected-mode no 

其他五個檔案分別複製redis_7001.conf檔案,檔名分別是:

  • redis_7002.conf
  • redis_7003.conf
  • redis_7004.conf
  • redis_7005.conf
  • redis_7006.conf

根據檔名修改修改portcluster-config-filelogfile三個屬性,比如redis_7002.conf的設定修改以下欄位:

port 7001
cluster-config-file nodes_7002.conf
logfile "redis_7002.log"

其他組態檔也修改成對應檔名的欄位。

啟動redis節點:

bin/redis-server redis_7001.conf &
bin/redis-server redis_7002.conf &
bin/redis-server redis_7003.conf &
bin/redis-server redis_7004.conf &
bin/redis-server redis_7005.conf &
bin/redis-server redis_7006.conf 

然後檢視redis程序:

[root@localhost redis]# ps -ef|grep redis
root     24783     1  0 Nov15 ?        00:07:53 bin/redis-server 0.0.0.0:7001 [cluster]
root     24792     1  0 Nov15 ?        00:07:50 bin/redis-server 0.0.0.0:7002 [cluster]
root     24805     1  0 Nov15 ?        00:07:53 bin/redis-server 0.0.0.0:7003 [cluster]
root     24816     1  0 Nov15 ?        00:07:49 bin/redis-server 0.0.0.0:7004 [cluster]
root     24821     1  0 Nov15 ?        00:07:53 bin/redis-server 0.0.0.0:7005 [cluster]
root     24830     1  0 Nov15 ?        00:07:50 bin/redis-server 0.0.0.0:7006 [cluster]

--cluster-replicas 1 參數列示建立一個主節點同時也建立一個從節點。

建立redis叢集:

redis-cli --cluster 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7001
Adding replica 127.0.0.1:7005 to 127.0.0.1:7002
Adding replica 127.0.0.1:7006 to 127.0.0.1:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 9f8616c497aeb89e065c9ed7e260a13a499078eb 127.0.0.1:7001
   slots:[0-5460] (5461 slots) master
M: 1064be46f6001390b47308fcb90832cb5eff3256 127.0.0.1:7002
   slots:[5461-10922] (5462 slots) master
M: c862b3f74904891972debe055edee66d08563f6c 127.0.0.1:7003
   slots:[10923-16383] (5461 slots) master
S: 51fa3d61cd6075d8a179ec5402c3d6771592d524 127.0.0.1:7004
   replicates c862b3f74904891972debe055edee66d08563f6c
S: f2a18a3fd5f7097888f31cbbc3878f26699ecd09 127.0.0.1:7005
   replicates 9f8616c497aeb89e065c9ed7e260a13a499078eb
S: 004d9acf71c448d93c8b3211f1fd132dd47cd5e9 127.0.0.1:7006
   replicates 1064be46f6001390b47308fcb90832cb5eff3256
Can I set the above configuration? (type 'yes' to accept): 

可以看到啟動六個節點,三個主節點 Master,三個從節點 Slave,以及他們之間的主從關係。六個節點,每個節點都生成一個唯一的編碼。

輸入yes

最後有以下輸出,表示叢集搭建成功:

[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

測試叢集

登入使用者端需要帶上引數-c表示叢集環境,否則只能獲取單個節點的資料。

先在7001新增資料

bin/redis-cli -p 7001
redis 127.0.0.1:7001> set name jeremy
OK

然後在7002獲取資料:

bin/redis-cli -p 7002
redis 127.0.0.1:7002> get name
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get name
"jeremy"

7002獲取資料,redis叢集會根據key計算雜湊槽的位置,算出資料在7001節點,重定向到7001節點獲取資料。

  • 新增新節點

新增一個新節點,一般是新增一個空節點,將其他節點資料移動該節點資料庫中。實現資料庫的擴容。

bin/redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7001

將新節點地址為第一個引數,叢集中隨機一個節點地址為第二個引數。上面的命令的表示將新節點127.0.0.1:7006新增到127.0.0.1:7001所在的叢集中。

  • 刪除節點
bin/redis-cli --cluster del-node 127.0.0.1:7001

叢集模式的優缺點

  • 優點
    • 具有高可用,哨兵模式的優點,他都有
    • 資料分片,不重複儲存資料,佔記憶體小
    • 容易實現擴容和縮容

總結

  • 主從模式:
    • 單機宕機或者磁碟出現故障,會導致資料丟失,主從模式將資料複製給多個從伺服器上,即使一臺資料庫宕機了,其他資料也能正常提供資料。
    • 主從模式有一臺主資料庫,多臺從資料庫的模式。使用者端對主資料庫進行讀寫,從資料庫只能讀操作。
    • 啟動主從資料庫之後,從資料庫傳送SYNC同步命令給主資料庫,主資料庫接收到命令之後,生成RDB檔案。並且使用緩衝區記錄所有寫命令。寫完畢後傳送RDB檔案給每個從資料庫解析,以及傳送快取寫命名給所以從資料庫執行。
    • 主資料庫更新資料後,資料會同步更新到從資料庫中。
    • 主從模式實現了簡單的可用,但是如果主資料庫宕機了,手動切換主資料庫比較費力,就有了哨兵模式。
  • 哨兵模式:
    • 根據主從模式無法自動切換問題,就有了哨兵模式。
    • 哨兵是一個獨立的程序,它主要有三個功能:監控資料庫,發現主資料庫宕機了,首先標記主觀下線,當主觀下線數量達到設定的數量時,就會進行投票,通過之後就執行切換主資料庫,將一個從資料庫升級成主資料庫。並通知給其他資料庫修改主資料庫設定。
    • 哨兵模式實現自動切換主資料庫,實現了服務的高可用。
    • 哨兵模式和主從模式一樣,所有資料庫都存放相同的資料,比較浪費記憶體,而且受限於單機資料庫,很難實現線上擴容。
  • Cluster模式
    • Redis叢集有16384個雜湊嘈,對鍵的CRC16取模16384計算出雜湊槽。
    • 叢集使用分片,使用節點方式,將雜湊槽分佈在每個節點上。也就講資料分佈儲存上不同的節點上。
    • 為了保證服務的高可用,每個節點都可以搭建主從。
    • 資料庫擴容需要新增節點,從新計算雜湊嘈,將其他資料庫的資料,轉移到新節點上。也可以刪除節點實現資料庫的縮容,刪除節點後,該節點的資料也會根據雜湊嘈分配到其他節點上。

參考

感覺不錯的話,點個贊吧!