在生產環境下,單機部署的Redis服務一旦宕機,所有依賴Redis服務的主流服務都會受到影響, 這時就需要一種Redis高可用方案。
一般來說, 一個高可用的方案要滿足以下三點要求:
- 數據備份(冗余) 數據冗余在不同的節點上,防止數據丟失
- 故障自動切換 正在服務的節點故障時,可以自動地切換到備用節點。
- 在線擴容(縮容) 即可以根據需要動態地增加、減少服務實例。
一、主從復制
類似於MYSQL的主從同步, 是將一台Redis服務器的數據(主節點)復制到其他的Redis服務器上(從節點),且數據的復制是單向的,只能由主節點到從節點。Redis 主從復制支持 主從同步 和 從從同步 兩種,后者是 Redis 后續版本新增的功能,以減輕主節點的同步負擔。
主從復制原理
1.slave節點初次啟動時主動向master發起TCP連接,並發起同步請求(psync命令), master接收連接(可要求授權認證),並將slave的信息保存起來。
2.master節點收到同步請求,執行BGSAVE命令生成rdb文件,文件生成后發送給slave。
3.slave收到后首先清楚自己的舊數據, 然后載入收到的rdb文件, slave更新至master執行bgsave命令前的狀態。
4.master將保存rdb文件期間收到的寫命令發送給slave, slave更新至主節點的最新狀態。
5.此后master每有寫命令,就會主動發送給slave節點。
主從節點會分別維護一個復制便宜量(復制的字節數), 當出現網絡中斷等情況時,重連后會從偏移量處開始進行部分復制,避免了全量復制的重型操作。
主從復制配置
主從復制master節點不需要做任何配置, 只需要在slave的配置文件中加入: slaveof <masterip> <masterport>
從節點啟動時就會自動向主節點發起連接,完成主從同步的一系列過程。
優點
主從復制提供了基本的數據多節點備份功能, 當主節點發生故障時,可以啟用從節點繼續提供服務。
缺點
無法實現故障的自動切換, 主節點故障時,需要手動將程序(客戶端)的配置從主節點切換為從節點,然后重啟客戶端程序。
Tips: 主從復制的機制是其他高可用方式的基礎, 下面介紹的哨兵方式和集群方式都依賴於主從復制機制。
二、哨兵
上圖 展示了一個典型的哨兵架構圖,它由兩部分組成,哨兵節點和數據節點(主節點+從節點)
- 哨兵節點: 哨兵系統由一個或多個哨兵節點組成,哨兵節點是特殊的 Redis 節點,不存儲數據。
- 數據節點: 主節點和從節點都是數據節點。
哨兵方式在主從復制的基礎上, 實現了故障自動切換的功能:
- 監控(Monitoring): 哨兵會不斷地檢查主節點和從節點是否運作正常。
- 自動故障轉移(Automatic failover): 當 主節點 不能正常工作時,哨兵會開始 自動故障轉移操作,它會將失效主節點的其中一個 從節點升級為新的主節點,並讓其他從節點改為復制新的主節點。
- 配置提供者(Configuration provider): 客戶端在初始化時,通過連接哨兵來獲得當前 Redis 服務的主節點地址。
- 通知(Notification): 哨兵可以將故障轉移的結果發送給客戶端。
快速開始
以下我們以一主二從三哨兵的架構來搭建一個哨兵系統
找到 redis.conf
文件復制三份分別命名為 redis-master.conf
redis-slave1.conf
redis-slave2.conf 分別作為一個主節點和兩個從節點的配置
配置如下:
#redis-master.conf master配置
port 6379
daemonize yes
logfile "6379.log"
dbfilename "dump-6379.rdb"
#redis-slave1.conf slave1配置
port 6380
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
slaveof 127.0.0.1 6379
#redis-slave2.conf slave2配置
port 6381
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
slaveof 127.0.0.1 6379
然后啟動三個redis實例:
redis-server redis-master.conf
redis-server redis-slave1.conf
redis-server redis-slave2.conf
節點啟動后,我們執行 redis-cli
默認連接到我們端口為 6379
的主節點執行 info Replication
檢查一下主從狀態是否正常
按照上面同樣的方法,我們給哨兵節點也創建三個配置文件。(哨兵節點本質上是特殊的 Redis 節點,所以配置幾乎沒什么差別,只是在端口上做區分就好)
# redis-sentinel-1.conf
port 26379
daemonize yes
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 6379 2
# redis-sentinel-2.conf
port 26380
daemonize yes
logfile "26380.log"
sentinel monitor mymaster 127.0.0.1 6379 2
# redis-sentinel-3.conf
port 26381
daemonize yes
logfile "26381.log"
sentinel monitor mymaster 127.0.0.1 6379 2
其中,sentinel monitor mymaster 127.0.0.1 6379 2
配置的含義是:該哨兵節點監控 127.0.0.1:6379
這個主節點,該主節點的名稱是 mymaster
,最后的 2
的含義與主節點的故障判定有關:至少需要 2
個哨兵節點同意,才能判定主節點故障並進行故障轉移。
執行下方命令將哨兵節點啟動起來:
redis-server redis-sentinel-1.conf --sentinel
redis-server redis-sentinel-2.conf --sentinel
redis-server redis-sentinel-3.conf --sentinel
使用 redis-cil
工具連接哨兵節點,並執行 info Sentinel
命令來查看是否已經在監視主節點了
# 連接端口為 26379 的 Redis 節點
➜ ~ redis-cli -p 26379
127.0.0.1:26379> info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
三、集群方式
哨兵方式雖然實現了故障自動切換, 但是實際為客戶端提供讀寫服務的Redis仍然只有主節點一個,所以受限於單機的內存容量。
- 集群方式采用數據分片的方式將數據存儲在多個節點上,突破了單機的存儲限制。
- 集群中的每個節點都可以對客戶端提供讀寫服務, 集群相對於單機擁有更高的並發能力。
上圖 展示了 Redis Cluster 典型的架構圖,集群中的每一個 Redis 節點都 互相兩兩相連,客戶端任意 直連 到集群中的 任意一台,就可以對其他 Redis 節點進行 讀寫 的操作。
數據分片
Redis 集群使用數據分片來實現, 一個 Redis 集群包含 16384 個哈希槽(hash slot), 數據庫中的每個鍵都屬於這 16384 個哈希槽的其中一個, 集群使用公式 CRC16(key) % 16384 來計算鍵 key 屬於哪個槽。集群中的每個節點負責處理一部分哈希槽。
搭建Redis集群
要讓集群正常工作至少需要3個主節點,在這里我們要創建6個redis節點,其中三個為主節點,三個為從節點。為了方便演示,這6個redis部署在同一台機器, 采用不同的端口號(7000 ~ 7005)。
准備配置文件
6個Redis節點的配置文件分別命名為node_7000.conf, node_7001.conf , ......, node_7005.conf,
除了端口號不同外,其余配置相同, 配置如下:
# 后台執行
daemonize yes
# 端口號
port 7000
# 為每一個集群節點指定一個 pid_file
pidfile ~/Desktop/redis-cluster/redis_7000.pid
# 啟動集群模式
cluster-enabled yes
# 每一個集群節點都有一個配置文件,這個文件是不能手動編輯的。確保每一個集群節點的配置文件不通
cluster-config-file nodes-7000.conf
# 集群節點的超時時間,單位:ms,超時后集群會認為該節點失敗
cluster-node-timeout 5000
# 最后將 appendonly 改成 yes(AOF 持久化)
appendonly yes
啟動6個Redis實例
redis-server redis_7000.conf
redis-server redis_7001.conf
redis-server redis_7002.conf
redis-server redis_7003.conf
redis-server redis_7004.conf
redis-server redis_7005.conf
使用ps -ef | grep redis查看
可以看到6個Redis實例都以cluster的方式啟動了
實例啟動后還處於各自獨立的狀態,還沒有形成集群,需要手動執行命令建立集群。
建立集群
執行命令:
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 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
--replicas 1
的意思是:我們希望為集群中的每個主節點創建一個從節點。
看到 [OK]
的信息之后,就表示集群已經搭建成功了,可以看到,這里我們正確地創建了三主三從的集群。
驗證集群
使用 redic-cli
任意連接一個節點:
redis-cli -c -h 127.0.0.1 -p 7000
127.0.0.1:7000>
-c
表示集群模式;-h
指定 ip 地址;-p
指定端口。
127.0.0.1:7000> SET name xiaoming
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK
127.0.0.1:7001>
可以看到這里 Redis 自動幫我們進行了 Redirected
操作跳轉到了 7001
這個實例上
我們可以在任意節點使用cluster nodes
查看節點列表
故障模擬
從圖中我們可以看到7005實例是7001實例的從節點, 我們現在停掉7001的實例(kill -9)
然后我們連接現存的任一節點,讀取name值
發現仍然可以從7005的實例上讀取到值,集群仍然是正常運轉的。
當我們把7005實例也停掉后,集群就會變得不可用