Redis 是一個高性能的鍵值存儲系統(tǒng),廣泛應用于緩存、消息隊列等場景。為了提高 Redis 的可用性和擴展性,Redis 提供了三種主要的高可用方案:主從模式、哨兵模式和集群模式。本文將詳細介紹這三種模式的工作原理、配置方法和適用場景。
1. Redis 主從模式(Master-Slave)
1.1 原理介紹
主從模式是 Redis 最基礎的高可用方案,通過讀寫分離和數(shù)據(jù)備份提高系統(tǒng)的可用性和讀取吞吐量。
核心特點:
- 讀寫分離:主節(jié)點(Master)處理寫請求,從節(jié)點(Slave)處理讀請求
- 數(shù)據(jù)備份:從節(jié)點實時同步主節(jié)點數(shù)據(jù),實現(xiàn)數(shù)據(jù)冗余
- 一主多從:一個主節(jié)點可以配置多個從節(jié)點
1.2 主從復制實現(xiàn)機制
復制ID和偏移量
- replication ID:主庫的 replication ID 就是自身 runid,是Redis服務啟動時自動生成的40位隨機字符串;從庫只負責記錄并保存主庫的 replication ID,自身 runid 不參與主從同步協(xié)商
- 復制偏移量(offset):主庫每執(zhí)行一個寫命令,會將命令追加到復制積壓緩沖區(qū),同時復制偏移量(offset)遞增;從庫同步執(zhí)行完寫命令后,也會更新自己的復制偏移量,用于標識已同步的數(shù)據(jù)位置
- 從庫連接攜帶信息:從庫建立或重連主庫時,會攜帶已記錄的主庫 replication ID 和自身已同步的復制偏移量 offset;主庫通過比對 replication ID 是否匹配、offset 是否在復制積壓緩沖區(qū)范圍內,判斷執(zhí)行全量同步還是增量同步
SYNC vs PSYNC
| 命令 | 說明 |
|---|---|
| SYNC | 全量同步,無論從節(jié)點之前是否同步過,每次都會重新生成 RDB 快照并傳輸全部數(shù)據(jù)(Redis 2.8 之前的舊機制) |
| PSYNC | 支持部分重同步,通過復制偏移量和備份 ID 判斷,僅傳輸斷線期間的增量數(shù)據(jù)(Redis 2.8+ 默認方式) |
全量同步(同步)
- 從服務器向主服務器發(fā)送
PSYNC命令,請求同步數(shù)據(jù) - 主服務器收到命令后,執(zhí)行
BGSAVE命令,在后臺生成 RDB 文件 - 主服務器將 RDB 文件發(fā)送給從服務器,同時將新的寫命令緩存在內存中
- 從服務器接收到 RDB 文件后,清空自己的數(shù)據(jù)庫,然后加載 RDB 文件到內存中
- 主服務器將緩存的寫命令發(fā)送給從服務器,從服務器執(zhí)行這些命令,保證數(shù)據(jù)一致性
注意:在全量同步階段,從服務器會等待主服務器發(fā)送 RDB 文件和緩存的寫命令,這個過程是同步的,從服務器在加載 RDB 文件和執(zhí)行緩存命令時,無法處理客戶端的請求。
觸發(fā)全量同步的場景:
- 從服務器首次連接主服務器:必須進行全量同步以獲取完整的數(shù)據(jù)副本
-
主從復制中斷時間過長:當斷開時間超過
repl-backlog-size緩沖區(qū)容量時,觸發(fā)全量同步 - 主服務器重啟:主服務器重啟后自身 runid(復制ID) 變更,從庫重連時攜帶舊的主庫復制ID,主庫無法匹配識別,觸發(fā)全量同步
注意:從服務器重啟僅自身 runid 變化,不影響復制協(xié)商,只要主庫復制ID未變、偏移量仍在復制積壓緩沖區(qū)范圍內,依舊可以走增量同步,不會觸發(fā)全量同步
增量同步(異步)
在全量同步完成后,主從關系建立,從節(jié)點不再發(fā)送 PSYNC 命令。增量同步階段,主節(jié)點主動持續(xù)推送寫命令給從節(jié)點,從節(jié)點不主動請求數(shù)據(jù)。
復制積壓緩沖區(qū)特點:
- 主庫統(tǒng)一管理:主庫維護一個復制積壓緩沖區(qū),所有從庫共享該緩沖區(qū)
-
大小可調整:通過
repl-backlog-size參數(shù)配置,默認 1MB,建議根據(jù)主庫寫命令頻率和從庫處理能力調整 - 環(huán)形緩沖區(qū):采用環(huán)形設計,緩沖區(qū)滿后新數(shù)據(jù)覆蓋最舊數(shù)據(jù)
- 增量同步條件:只有當從節(jié)點的偏移量仍在緩沖區(qū)范圍內,才能增量同步;若偏移量被覆蓋,主庫直接推送緩沖區(qū)中的后續(xù)命令,從庫會跳過丟失的命令,導致數(shù)據(jù)丟失但最終與主庫保持當前數(shù)據(jù)一致
重要提醒:正常連接時也可能發(fā)生丟數(shù)據(jù),不只是斷開重連才丟。當從庫處理速度較慢(如網(wǎng)絡延遲、從庫負載高等),即使連接正常,從庫上報的 offset 也可能被新命令覆蓋,導致丟段但不觸發(fā)全量,主從最終一致但歷史操作丟失,只有下次全量復制時才會補充。因此平時要合理設置 repl-backlog-size,避免正常復制過程中因緩沖區(qū)過小導致數(shù)據(jù)丟失。
觸發(fā)增量同步的場景:
- 全量同步結束后自動進入,無需額外協(xié)商
- 主從復制正常運行期間,主服務器持續(xù)將寫命令同步給從服務器
- 主從連接短暫中斷后恢復,且中斷期間的數(shù)據(jù)變更仍在 repl-backlog 緩沖區(qū)中
心跳檢測
全量同步完成后,從節(jié)點通過心跳機制與主節(jié)點保持連接:
心跳消息:
- 從節(jié)點發(fā)送 PING(默認每秒一次),攜帶自身當前的復制偏移量
- 主節(jié)點響應 PONG,僅表示連接正常,不攜帶偏移量信息
重要結論:
- 偏移量單向傳遞:只有從 → 主,主節(jié)點不會反向發(fā)送自己的偏移量給從節(jié)點
- 心跳是同步的依據(jù):主庫在正常運行時會主動推送新命令,但推送的前提和依據(jù),完全來自從庫每秒心跳上報的偏移量;沒有心跳,主庫既不知道推到哪,也不知道從庫缺了啥
- 主節(jié)點判斷并推送:主節(jié)點根據(jù)從節(jié)點匯報的偏移量,自行判斷從節(jié)點缺了哪些命令,然后主動推送過去,從節(jié)點不請求數(shù)據(jù)
增量同步流程:
主節(jié)點執(zhí)行寫命令
↓
命令追加到 repl-backlog 緩沖區(qū)
↓
主節(jié)點根據(jù)從節(jié)點匯報的 偏移量 判斷缺失數(shù)據(jù)
↓
主節(jié)點 主動推送 缺失的命令給從節(jié)點
↓
從節(jié)點接收并執(zhí)行命令,更新偏移量
注意:PSYNC 命令只在建立復制關系或重連時發(fā)送一次,由主節(jié)點根據(jù) replication ID 和 offset 決定是全量同步還是增量同步。全量同步結束后自動進入增量同步階段,無需額外協(xié)商。
1.3 配置示例
主節(jié)點配置
# 監(jiān)聽端口
port 6379
# 設置密碼認證
requirepass your_master_password
# 連接主節(jié)點時的密碼(用于主從復制)
masterauth your_master_password
# 允許所有IP連接(生產(chǎn)環(huán)境建議指定具體IP)
bind 0.0.0.0
# 后臺運行
daemonize yes
從節(jié)點配置
# 監(jiān)聽端口
port 6380
# 指定主節(jié)點地址和端口(主從復制配置,Redis 5.0+ 推薦使用 replicaof)
replicaof 127.0.0.1 6379
# 連接主節(jié)點時使用的密碼
masterauth your_master_password
# 從節(jié)點密碼
requirepass your_slave_password
# 允許所有IP連接
bind 0.0.0.0
# 后臺運行
daemonize yes
1.4 Spring Boot 配置
配置方式:手動寫死主節(jié)點和從節(jié)點地址
讀寫控制:客戶端代碼自己控制寫主、讀從
spring:
redis:
password: your_master_password
database: 0
master:
host: 127.0.0.1
port: 6379
slaves:
hosts: 127.0.0.1:6380,127.0.0.1:6381 # 多從節(jié)點
@Configuration
public class RedisConfig {
@Value("${spring.redis.master.host}")
private String masterHost;
@Value("${spring.redis.master.port}")
private int masterPort;
@Value("${spring.redis.slaves.hosts}")
private String slaves;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private int database;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
// 主節(jié)點
RedisStandaloneConfiguration master = new RedisStandaloneConfiguration(masterHost, masterPort);
master.setPassword(password);
master.setDatabase(database);
// 從節(jié)點列表
List<RedisStandaloneConfiguration> slavesList = Arrays.stream(slaves.split(","))
.map(s -> {
String[] hp = s.split(":");
RedisStandaloneConfiguration c = new RedisStandaloneConfiguration(hp[0], Integer.parseInt(hp[1]));
c.setPassword(password);
c.setDatabase(database);
return c;
}).toList();
// 關鍵:創(chuàng)建主從拓撲
MasterSlaveConnectionFactory factory = new MasterSlaveConnectionFactory(master, slavesList);
factory.setReadFrom(ReadFrom.REPLICA_PREFERRED); // 現(xiàn)在才真正生效!
return factory;
}
}
1.5 主從模式優(yōu)缺點
| 優(yōu)點 | 缺點 |
|---|---|
| 配置簡單,易于上手 | 主節(jié)點是單點,故障后需要人工干預(重啟主節(jié)點/將從節(jié)點升級為主節(jié)點等等) |
| 實現(xiàn)讀寫分離,提高讀取吞吐量 | 主從同步時數(shù)據(jù)可能短暫不一致 |
| 數(shù)據(jù)冗余,提高數(shù)據(jù)安全性 | 無法自動實現(xiàn)故障轉移 |
| 適合數(shù)據(jù)量小的場景 | 垂直擴展受單機硬件限制 |
2. Redis 哨兵模式(Sentinel Mode)
2.1 原理介紹
哨兵模式是基于主從模式的高可用方案,通過部署多個哨兵(Sentinel)進程來監(jiān)控主從節(jié)點的運行狀態(tài),并在主節(jié)點故障時自動完成故障轉移。
核心功能:
- 監(jiān)控(Monitoring):Sentinel 持續(xù)檢查主服務器和從服務器是否正常運行
- 通知(Notification):當被監(jiān)控的 Redis 實例出現(xiàn)問題時,Sentinel 通過 API 向管理員發(fā)送通知
- 自動故障轉移(Automatic Failover):當主服務器不能正常工作時,Sentinel 自動將一個從服務器升級為新的主服務器,并讓其他從服務器開始復制新的主服務器
- 配置提供者(Configuration Provider):客戶端連接 Sentinel 獲取當前 Redis 主節(jié)點的地址
哨兵模式本質上是一個運行在特殊模式下的 Redis 進程,用于監(jiān)控主從結構中的各個實例。通常建議至少部署 3 個哨兵實例以確保高可用性。
2.2 哨兵模式工作原理
- 主節(jié)點監(jiān)控:哨兵節(jié)點通過配置文件指定要監(jiān)控的主節(jié)點,自動發(fā)現(xiàn)關聯(lián)的從節(jié)點
-
哨兵互相發(fā)現(xiàn):哨兵彼此不靠配置,靠主庫上的 Pub/Sub 頻道(
__sentinel__:hello)互相發(fā)現(xiàn),通過定期發(fā)布消息來感知其他哨兵的存在 - 多哨兵協(xié)作:多個哨兵節(jié)點協(xié)同工作,通過投票機制判斷主節(jié)點是否失效
- 故障檢測:當主節(jié)點在指定時間內沒有回復 PING,則認為它主觀下線,達到 quorum 配置值的多個哨兵確認后認定為客觀下線
- 故障轉移:從剩余的從節(jié)點中選舉一個新的主節(jié)點,并更新其他從節(jié)點的復制目標
2.3 故障轉移詳解
主觀下線(SDown)
↓
多個哨兵確認(達到 quorum)
↓
客觀下線(ODown)
↓
哨兵投票選舉leader
↓
選舉從節(jié)點成為新主節(jié)點
↓
更新從節(jié)點復制目標
↓
舊主節(jié)點降級為從節(jié)點
故障轉移過程:
- 當主節(jié)點被認定為客觀下線后,所有監(jiān)視該主節(jié)點的哨兵進行投票
- 被選為 leader 的哨兵負責執(zhí)行故障轉移
- 從所有從節(jié)點中選舉一個成為新的主節(jié)點(選舉規(guī)則:優(yōu)先級 > 偏移量 > runid)
- leader 哨兵讓其他從節(jié)點去復制新的主節(jié)點
- 哨兵自動將恢復的原主節(jié)點降級為從節(jié)點,使其追隨新選舉的主節(jié)點
2.4 配置示例
主節(jié)點配置
port 6379
requirepass your_master_password
masterauth your_master_password
bind 0.0.0.0
daemonize yes
從節(jié)點配置
port 6380
slaveof 127.0.0.1 6379
masterauth your_master_password
requirepass your_slave_password
bind 0.0.0.0
daemonize yes
哨兵配置
# 哨兵監(jiān)聽端口
port 26379
# 監(jiān)控的主節(jié)點名稱、IP、端口和法定人數(shù)
# 格式:sentinel monitor <mastername> <ip> <port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# 主節(jié)點密碼認證
sentinel auth-pass mymaster your_master_password
# 判斷主節(jié)點主觀下線的毫秒數(shù)
sentinel down-after-milliseconds mymaster 5000
# 故障轉移時,同時進行復制的從節(jié)點數(shù)量
sentinel parallel-syncs mymaster 1
# 故障轉移超時時間(毫秒)
sentinel failover-timeout mymaster 10000
# 允許所有IP連接
bind 0.0.0.0
# 后臺運行
daemonize yes
2.5 Spring Boot 配置
配置方式:只寫2~3個哨兵種子節(jié)點
讀寫控制:客戶端問哨兵拿主節(jié)點寫主;自動發(fā)現(xiàn)所有從節(jié)點讀從
spring:
redis:
# 哨兵模式配置
sentinel:
# 主節(jié)點名稱
master: mymaster
# 哨兵節(jié)點列表
nodes:
- 127.0.0.1:26379
- 127.0.0.1:26380
- 127.0.0.1:26381
# Redis密碼
password: your_master_password
# 連接超時時間
timeout: 2000ms
# 數(shù)據(jù)庫索引(默認0)
database: 0
# Lettuce配置(讀寫分離)
lettuce:
client-options:
read-from: REPLICA_PREFERRED # 優(yōu)先從從節(jié)點讀
2.6 哨兵模式優(yōu)缺點
| 優(yōu)點 | 缺點 |
|---|---|
| 自動故障轉移,無需人工干預 | 不支持數(shù)據(jù)分片,所有數(shù)據(jù)存儲在主節(jié)點 |
| 客戶端無需知道主節(jié)點地址,通過哨兵獲取 | 故障轉移期間可能出現(xiàn)短時不可用 |
| 每個哨兵節(jié)點都可以提供故障轉移能力 | 仍然基于主從復制,數(shù)據(jù)同步存在延遲 |
| 適合數(shù)據(jù)量不大但需要高可用的場景 | 最小需要 5 個節(jié)點(1主1從+3哨兵) |
3. Redis 集群模式(Cluster Mode)
3.1 原理介紹
集群模式是 Redis 的分布式解決方案,通過數(shù)據(jù)分片和高可用機制實現(xiàn)水平擴展和故障轉移。
核心特點:
- 數(shù)據(jù)分片:數(shù)據(jù)自動分片到多個節(jié)點上,每個節(jié)點負責一部分哈希槽(hash slot)
- 高可用性:每個主節(jié)點可以有多個從節(jié)點,實現(xiàn)主從復制和自動故障轉移
- 去中心化:集群中的每個節(jié)點都保存著整個集群的狀態(tài)信息,沒有中心節(jié)點
- 客戶端路由:客戶端可以連接任意節(jié)點,節(jié)點會將請求重定向到正確的節(jié)點
3.2 為什么集群模式不需要哨兵
Redis Cluster 與哨兵模式相比,具有以下關鍵差異,因此不需要額外的哨兵組件:
-
內置高可用性:
- Redis Cluster 本身已經(jīng)內置了故障檢測和自動故障轉移機制
- 每個節(jié)點都參與集群狀態(tài)的維護和故障檢測
- 不需要外部組件來監(jiān)控節(jié)點狀態(tài)
-
去中心化架構:
- 集群中的每個節(jié)點都保存完整的集群狀態(tài)信息
- 節(jié)點之間通過 Gossip 協(xié)議交換狀態(tài)信息
- 沒有單點故障問題
-
自動主從管理:
- 集群在創(chuàng)建時就配置了主從關系
- 當主節(jié)點失效時,相關從節(jié)點會自動進行故障轉移
- 整個過程由集群內部協(xié)議處理,無需外部干預
-
分布式?jīng)Q策:
- 故障檢測和主從切換由集群內多數(shù)節(jié)點共同決定
- 避免了哨兵模式中可能出現(xiàn)的網(wǎng)絡分區(qū)問題
故障轉移機制詳解
Redis Cluster 的故障轉移流程如下:
主觀下線(PFAIL)
↓
超過半數(shù)主節(jié)點確認
↓
客觀下線(FAIL)
↓
從節(jié)點發(fā)起選舉
↓
主節(jié)點投票
↓
獲得超過半數(shù)票的從節(jié)點成為新主節(jié)點
| 階段 | 發(fā)起者 | 參與者 | 說明 |
|---|---|---|---|
| 主觀下線(PFAIL) | 任何節(jié)點(主/從) | - | 節(jié)點通過 PING 檢測到某節(jié)點超時未響應,標記為 PFAIL(Possible Fail) |
| 客觀下線(FAIL) | 發(fā)現(xiàn) PFAIL 的節(jié)點 | 主節(jié)點投票 | 當超過半數(shù)主節(jié)點認為某主節(jié)點下線時,將其標記為 FAIL |
| 選舉發(fā)起 | 從節(jié)點 | - | 從節(jié)點發(fā)現(xiàn)主節(jié)點被標記為 FAIL 后,發(fā)起選舉請求 |
| 投票選舉 | - | 主節(jié)點投票 | 主節(jié)點投票,獲得超過半數(shù)票的從節(jié)點成為新的主節(jié)點 |
關鍵點:
- 所有節(jié)點都參與集群狀態(tài)維護和故障檢測(通過 Gossip 協(xié)議交換狀態(tài))
- 主觀下線:任何節(jié)點都可以標記
- 客觀下線:只有主節(jié)點有投票權
- 選舉發(fā)起:從節(jié)點發(fā)起選舉請求
- 投票:只有主節(jié)點參與投票
為什么主節(jié)點數(shù)量必須 ≥ 3?
"超過半數(shù)"的硬性要求是基于 Raft 協(xié)議 的分布式一致性原則,不可配置。這直接決定了主節(jié)點數(shù)量必須 ≥ 3。
假設只有 2 個主節(jié)點:
| 主節(jié)點 | 狀態(tài) | 投票權 |
|---|---|---|
| M1 | ? 宕機 | 無法投票 |
| M2 | ? 正常 | 1 票 |
- 總票數(shù):2 票
- M2 的票數(shù):1 票(50%)
- 需要超過半數(shù):> 1 票,即至少 2 票
- 結果:選舉失敗,無法選出新主節(jié)點 ?
集群是否可用? 取決于 cluster-require-full-coverage 配置:
| 配置值 | 結果 |
|---|---|
yes(默認) |
集群完全不可用,任何請求都返回錯誤 |
no |
部分可用,M1 負責的槽位不可用,M2 負責的槽位仍可正常服務 |
不同主節(jié)點數(shù)量的容錯能力:
| 主節(jié)點數(shù) | 掛掉 1 個后 | 剩余票數(shù) | 需要票數(shù)(> N/2) | 能否選舉 | 最大容錯節(jié)點數(shù) |
|---|---|---|---|---|---|
| 2 | 1 個 | 1 票 | > 1(需 2 票) | ? 不能 | 0 |
| 3 | 2 個 | 2 票 | > 1.5(需 2 票) | ? 能 | 1 |
| 5 | 4 個 | 4 票 | > 2.5(需 3 票) | ? 能 | 2 |
| 7 | 6 個 | 6 票 | > 3.5(需 4 票) | ? 能 | 3 |
結論:
- 主節(jié)點數(shù)量必須是奇數(shù)(3、5、7...),因為偶數(shù)并不能提高容錯能力
- 3 個主節(jié)點可容忍 1 個節(jié)點故障
- 5 個主節(jié)點可容忍 2 個節(jié)點故障
- 生產(chǎn)環(huán)境推薦 3 個主節(jié)點,足以滿足大多數(shù)場景的高可用需求
3.3 自動分配主從節(jié)點機制
Redis Cluster 在創(chuàng)建時會自動分配主從節(jié)點關系:
-
節(jié)點角色分配:
- 在創(chuàng)建集群時,通過
--cluster-replicas參數(shù)指定每個主節(jié)點的從節(jié)點數(shù)量 - 系統(tǒng)自動將節(jié)點分為主節(jié)點和從節(jié)點
- 在創(chuàng)建集群時,通過
-
槽位分配:
- 16384 個哈希槽被均勻分配給主節(jié)點
- 每個從節(jié)點知道自己對應的主節(jié)點
-
故障轉移機制:
- 當主節(jié)點失效時,其從節(jié)點會通過選舉產(chǎn)生新的主節(jié)點
- 其他從節(jié)點會更新復制目標到新的主節(jié)點
3.4 數(shù)據(jù)分片機制
Redis Cluster 采用哈希槽機制實現(xiàn)數(shù)據(jù)分片:
- 哈希槽劃分:整個集群被劃分為 16384 個哈希槽(0-16383)
- 鍵到槽映射:每個 key 通過 CRC16 算法計算哈希值,然后對 16384 取模得到槽位號
slot = CRC16(key) % 16384
- 槽位分配:這些槽位會均勻分布在不同的主節(jié)點上
槽位分配示例:
| 主節(jié)點數(shù) | 槽位分配 |
|---|---|
| 3主 | 0-5460 (5461槽), 5461-10921 (5461槽), 10922-16383 (5462槽) |
| 4主 | 0-4095 (4096槽), 4096-8191 (4096槽), 8192-12287 (4096槽), 12288-16383 (4096槽) |
| 6主 | 0-2730 (2731槽), 2731-5461 (2731槽), 5462-8192 (2731槽), 8193-10922 (2730槽), 10923-13653 (2731槽), 13654-16383 (2730槽) |
注意:16384 無法被 3 和 6 整除,因此槽位分配會有 1-2 個槽的差異。
Hash Tag 機制
當 key 包含 { 和 } 時,只有花括號內的部分會被用于哈希計算:
-
user:1000:profile和user:1000:settings→ 不同槽位 -
{user:1000}:profile和{user:1000}:settings→ 相同槽位
應用場景: 保證相關數(shù)據(jù)存儲在同一節(jié)點上,支持多鍵操作(如 MGET {user:1000}:profile {user:1000}:settings)
3.5 集群模式工作原理
-
節(jié)點發(fā)現(xiàn):通過
MEET消息發(fā)現(xiàn)新節(jié)點 -
槽位分配:通過
CLUSTER ADDSLOTS命令分配槽位 - 狀態(tài)同步:節(jié)點間通過 Gossip 協(xié)議交換集群狀態(tài)(IP、端口、運行ID、槽位信息)
-
請求路由:
- 節(jié)點存儲映射:每個節(jié)點都存儲完整的哈希槽與節(jié)點的對應關系
- 客戶端緩存映射:客戶端也會緩存一份哈希槽映射關系,負責將請求直接發(fā)送到正確的節(jié)點
- MOVED 重定向:當集群擴容/縮容導致客戶端緩存的舊映射失效,請求發(fā)送到錯誤的節(jié)點時,節(jié)點會返回 MOVED 重定向,客戶端據(jù)此更新映射關系并重新發(fā)送請求到正確的節(jié)點
- 故障檢測:通過 PING/PONG 消息檢測節(jié)點狀態(tài)
- 故障轉移:從節(jié)點檢測到主節(jié)點失效后發(fā)起選舉,成為新的主節(jié)點
3.6 客戶端類型
Redis 集群客戶端分為智能客戶端和非智能客戶端兩種類型:
| 類型 | 特點 | 性能 |
|---|---|---|
| 非智能客戶端 | 不緩存槽位映射,每次請求可能需要重定向 | 較低,多次網(wǎng)絡往返 |
| 智能客戶端 | 緩存槽位映射,直接將請求發(fā)送到正確節(jié)點 | 較高,減少重定向次數(shù) |
智能客戶端工作流程
- 初始化:啟動時從集群節(jié)點獲取完整的槽位映射關系
- 本地緩存:緩存哈希槽與節(jié)點的對應關系
- 直接路由:根據(jù)緩存的映射,直接將請求發(fā)送到正確的節(jié)點
-
自動更新:收到
MOVED或ASK響應時,自動更新本地緩存
Spring Boot 客戶端
Spring Boot 默認使用 Lettuce 作為 Redis 客戶端(也可選擇 Jedis),這兩種客戶端在集群模式下默認都是智能客戶端:
- Lettuce:基于 Netty 的異步客戶端,支持連接池,默認智能路由
- Jedis:傳統(tǒng)同步客戶端,支持連接池,默認智能路由
spring:
redis:
cluster:
nodes: 127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002
max-redirects: 3
# Lettuce 連接池配置(可選)
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
3.7 集群創(chuàng)建命令
# 創(chuàng)建集群(3主3從,每個主節(jié)點1個從節(jié)點)
redis-cli --cluster create 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 --cluster-replicas 1
3.8 配置示例
節(jié)點配置
# 節(jié)點監(jiān)聽端口
port 7000
# 開啟集群模式
cluster-enabled yes
# 集群配置文件(自動生成和維護)
cluster-config-file nodes-7000.conf
# 節(jié)點間通信超時時間(毫秒)
cluster-node-timeout 15000
# 是否要求所有槽位都被覆蓋才提供服務
cluster-require-full-coverage no
# 設置密碼認證
requirepass your_cluster_password
# 連接主節(jié)點時的密碼(用于主從復制)
masterauth your_cluster_password
# 允許所有IP連接
bind 0.0.0.0
# 后臺運行
daemonize yes
3.9 Spring Boot 配置
配置方式:只寫2~3個集群種子節(jié)點
讀寫控制:客戶端自動拉取全集群拓撲,寫主節(jié)點、讀從節(jié)點自動路由
spring:
redis:
# 集群模式配置
cluster:
# 集群節(jié)點地址列表(只需寫2~3個種子節(jié)點)
nodes:
- 127.0.0.1:7000
- 127.0.0.1:7001
- 127.0.0.1:7002
# 最大重定向次數(shù)
max-redirects: 3
# Redis密碼
password: your_cluster_password
# 連接超時時間
timeout: 2000ms
# Lettuce配置(讀寫分離)
lettuce:
client-options:
read-from: REPLICA_PREFERRED # 優(yōu)先從從節(jié)點讀
3.10 集群模式優(yōu)缺點
| 優(yōu)點 | 缺點 |
|---|---|
| 支持數(shù)據(jù)分片,實現(xiàn)水平擴展 | 多鍵操作受限,僅支持同一槽位 |
| 內置高可用,無需額外組件 | 客戶端需要支持集群協(xié)議 |
| 每個節(jié)點都參與集群管理,無單點故障 | 事務支持僅限同一節(jié)點 |
| 適合數(shù)據(jù)量大,需要分片+高可用的場景 | 最小需要 6 個節(jié)點(3主3從) |
多鍵操作限制詳解
集群模式下,不同類型的操作對跨槽的處理方式不同:
| 操作類型 | 跨槽支持 | 說明 |
|---|---|---|
| 普通批量命令(MGET/MSET/DEL) | ? 必須同槽 | 跨槽直接報錯:CROSSSLOT Keys in request don't hash to the same slot
|
| Hash Tag | ? 強制同槽 | 使用 {key} 確保相關 key 在同一槽位,性能最好 |
| Pipeline | ?? 可跨槽 | 客戶端自動處理重定向,性能較差,無原子性保證 |
| Lua 腳本 / 事務 | ? 必須同槽 | 與普通批量命令相同,必須在同一個節(jié)點上執(zhí)行 |
Hash Tag 示例:
# ? 報錯:user:100 和 user:200 可能不在同一槽位
MGET user:100 user:200
# ? 正常:{user} 決定槽位,profile 和 settings 都在同一槽位
MGET {user}:profile {user}:settings
選擇建議:
- 性能優(yōu)先:使用 Hash Tag 確保同槽
- 開發(fā)便利性:可以使用 Pipeline,但需注意性能損耗
- 強一致性:Lua 腳本和事務必須同槽
3.11 數(shù)據(jù)傾斜問題
現(xiàn)象
熱點槽、熱點大 Key、熱點讀/寫集中在單節(jié)點,導致節(jié)點 CPU / 內存 / 網(wǎng)卡打滿,集群負載失衡。
成因
| 成因 | 說明 |
|---|---|
| 固定 HashTag | 過度使用同一個 HashTag,導致大量 key 集中在一個槽位 |
| 業(yè)務分區(qū)鍵單一 | 業(yè)務設計時只用一個字段做 hash,無法分散到不同節(jié)點 |
| 冷熱數(shù)據(jù)未分離 | 熱點數(shù)據(jù)和冷數(shù)據(jù)混在一起,熱點數(shù)據(jù)占用大量資源 |
| 大 Key 集中 | 單個 key 的 value 特別大,導致該節(jié)點負載過高 |
優(yōu)化方案
1. 打散熱點 Key
-
增加隨機后綴:將熱點 key 拆分為多個副本,分散到不同槽位
hot:user:100 → hot:user:100:0, hot:user:100:1, hot:user:100:2 -
結合 HashTag:使用 HashTag 確保同槽操作
{user:100}:profile:{0}, {user:100}:profile:{1}, {user:100}:profile:{2}
2. 拆分大 Key
| 類型 | 優(yōu)化方法 |
|---|---|
| 大 Hash | 拆分為多個小 Hash,使用 HashTag 確保同槽 |
| 大 List | 拆分為多個 List,按范圍或 hash 分桶 |
| 大 Set | 按數(shù)據(jù)類型或時間維度分桶 |
3. 冷熱分離
- 熱點數(shù)據(jù)放 Redis:頻繁訪問的數(shù)據(jù)保持在 Redis
- 冷數(shù)據(jù)落庫:不常訪問的數(shù)據(jù)定期持久化到數(shù)據(jù)庫
- 本地緩存兜底:讀多寫少場景,使用本地緩存(如 Caffeine、Guava Cache)減輕 Redis 壓力
4. 監(jiān)控告警
| 監(jiān)控項 | 告警閾值建議 |
|---|---|
| 槽位負載 | 單槽 key 數(shù)量超過平均值 200% |
| Key 大小 | 單個 key 超過 10MB |
| 節(jié)點 CPU | 節(jié)點 CPU 使用率超過 80% |
| 節(jié)點內存 | 節(jié)點內存使用率超過 80% |
監(jiān)控命令:
# 查看各節(jié)點 key 數(shù)量
redis-cli --cluster info 127.0.0.1:7000
# 查看槽位分布
redis-cli --cluster check 127.0.0.1:7000
# 掃描大 key(Redis 4.0+)
redis-cli --bigkeys -i 0.1
4. 三種模式對比
| 特性 | 主從模式 | 哨兵模式 | 集群模式 |
|---|---|---|---|
| 數(shù)據(jù)分片 | 不支持 | 不支持 | 支持(16384個哈希槽) |
| 擴展性 | 垂直擴展 | 垂直擴展 | 水平擴展 |
| 高可用 | 無(需要人工干預) | 自動故障轉移 | 自動故障轉移 |
| 主從管理 | 手動配置 | 哨兵自動管理 | 集群自動管理 |
| 客戶端復雜度 | 簡單 | 簡單 | 需要支持集群協(xié)議 |
| 多鍵操作 | 完全支持 | 完全支持 | 僅支持同一槽位 |
| 事務支持 | 完全支持 | 完全支持 | 僅限同一節(jié)點 |
| 最小節(jié)點數(shù) | 2個(1主1從) | 5個(1主1從+3哨兵) | 6個(3主3從) |
| 適用場景 | 數(shù)據(jù)量小,基礎讀寫分離 | 數(shù)據(jù)量小,需要高可用 | 數(shù)據(jù)量大,需要分片+高可用 |
核心區(qū)別總結
| 模式 | 配置方式 | 讀寫控制 | 核心特點 |
|---|---|---|---|
| 主從模式 | 手動寫死主從節(jié)點地址 | 客戶端代碼自己控制寫主、讀從 | Redis 只負責數(shù)據(jù)同步,不負責路由和故障轉移 |
| 哨兵模式 | 只寫2~3個哨兵種子節(jié)點 | 客戶端問哨兵拿主節(jié)點寫主;自動發(fā)現(xiàn)所有從節(jié)點讀從 | 哨兵負責故障轉移、主從切換 |
| 集群模式 | 只寫2~3個集群種子節(jié)點 | 客戶端自動拉取全集群拓撲,寫主節(jié)點、讀從節(jié)點自動路由 | 分片 + 高可用,自帶故障轉移 |
一句話口訣:
主從自己寫,哨兵找主從,集群全自動。
5. 總結
Redis 的三種高可用模式適用于不同的場景:
主從模式:適用于數(shù)據(jù)量不大,只需要讀寫分離和數(shù)據(jù)備份的場景。配置簡單,但主節(jié)點故障需要人工干預。
哨兵模式:在主從模式基礎上增加了自動故障轉移功能,適用于數(shù)據(jù)量不大但需要高可用的場景。缺點是不支持數(shù)據(jù)分片。
集群模式:通過數(shù)據(jù)分片實現(xiàn)水平擴展,同時具備高可用機制,適用于數(shù)據(jù)量大、需要分片和分片內高可用的現(xiàn)代分布式應用。
重要結論:
- 哨兵模式和集群模式在主從復制的基本機制上是相似的,都基于 PSYNC2 協(xié)議,都支持全量和增量同步
- 集群模式內置了高可用機制,不需要額外的哨兵組件
- 選擇哪種模式主要取決于數(shù)據(jù)量大小、性能要求和系統(tǒng)復雜度等因素
推薦:
- 小規(guī)模應用(數(shù)據(jù)量 < 10GB):使用哨兵模式
- 大規(guī)模應用(數(shù)據(jù)量 > 10GB):使用集群模式
- 集群模式因為其內置的高可用性和自動主從管理機制,更適合現(xiàn)代分布式應用的需求。