Redis 主從、哨兵與集群模式詳解

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+ 默認方式)

全量同步(同步)

  1. 從服務器向主服務器發(fā)送 PSYNC 命令,請求同步數(shù)據(jù)
  2. 主服務器收到命令后,執(zhí)行 BGSAVE 命令,在后臺生成 RDB 文件
  3. 主服務器將 RDB 文件發(fā)送給從服務器,同時將新的寫命令緩存在內存中
  4. 從服務器接收到 RDB 文件后,清空自己的數(shù)據(jù)庫,然后加載 RDB 文件到內存中
  5. 主服務器將緩存的寫命令發(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 IDoffset 決定是全量同步還是增量同步。全量同步結束后自動進入增量同步階段,無需額外協(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 哨兵模式工作原理

  1. 主節(jié)點監(jiān)控:哨兵節(jié)點通過配置文件指定要監(jiān)控的主節(jié)點,自動發(fā)現(xiàn)關聯(lián)的從節(jié)點
  2. 哨兵互相發(fā)現(xiàn):哨兵彼此不靠配置,靠主庫上的 Pub/Sub 頻道(__sentinel__:hello)互相發(fā)現(xiàn),通過定期發(fā)布消息來感知其他哨兵的存在
  3. 多哨兵協(xié)作:多個哨兵節(jié)點協(xié)同工作,通過投票機制判斷主節(jié)點是否失效
  4. 故障檢測:當主節(jié)點在指定時間內沒有回復 PING,則認為它主觀下線,達到 quorum 配置值的多個哨兵確認后認定為客觀下線
  5. 故障轉移:從剩余的從節(jié)點中選舉一個新的主節(jié)點,并更新其他從節(jié)點的復制目標

2.3 故障轉移詳解

主觀下線(SDown)
    ↓
多個哨兵確認(達到 quorum)
    ↓
客觀下線(ODown)
    ↓
哨兵投票選舉leader
    ↓
選舉從節(jié)點成為新主節(jié)點
    ↓
更新從節(jié)點復制目標
    ↓
舊主節(jié)點降級為從節(jié)點

故障轉移過程:

  1. 當主節(jié)點被認定為客觀下線后,所有監(jiān)視該主節(jié)點的哨兵進行投票
  2. 被選為 leader 的哨兵負責執(zhí)行故障轉移
  3. 從所有從節(jié)點中選舉一個成為新的主節(jié)點(選舉規(guī)則:優(yōu)先級 > 偏移量 > runid
  4. leader 哨兵讓其他從節(jié)點去復制新的主節(jié)點
  5. 哨兵自動將恢復的原主節(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 與哨兵模式相比,具有以下關鍵差異,因此不需要額外的哨兵組件

  1. 內置高可用性

    • Redis Cluster 本身已經(jīng)內置了故障檢測自動故障轉移機制
    • 每個節(jié)點都參與集群狀態(tài)的維護和故障檢測
    • 不需要外部組件來監(jiān)控節(jié)點狀態(tài)
  2. 去中心化架構

    • 集群中的每個節(jié)點都保存完整的集群狀態(tài)信息
    • 節(jié)點之間通過 Gossip 協(xié)議交換狀態(tài)信息
    • 沒有單點故障問題
  3. 自動主從管理

    • 集群在創(chuàng)建時就配置了主從關系
    • 當主節(jié)點失效時,相關從節(jié)點會自動進行故障轉移
    • 整個過程由集群內部協(xié)議處理,無需外部干預
  4. 分布式?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é)點關系:

  1. 節(jié)點角色分配

    • 在創(chuàng)建集群時,通過 --cluster-replicas 參數(shù)指定每個主節(jié)點的從節(jié)點數(shù)量
    • 系統(tǒng)自動將節(jié)點分為主節(jié)點和從節(jié)點
  2. 槽位分配

    • 16384 個哈希槽被均勻分配給主節(jié)點
    • 每個從節(jié)點知道自己對應的主節(jié)點
  3. 故障轉移機制

    • 當主節(jié)點失效時,其從節(jié)點會通過選舉產(chǎn)生新的主節(jié)點
    • 其他從節(jié)點會更新復制目標到新的主節(jié)點

3.4 數(shù)據(jù)分片機制

Redis Cluster 采用哈希槽機制實現(xiàn)數(shù)據(jù)分片:

  1. 哈希槽劃分:整個集群被劃分為 16384 個哈希槽(0-16383)
  2. 鍵到槽映射:每個 key 通過 CRC16 算法計算哈希值,然后對 16384 取模得到槽位號
slot = CRC16(key) % 16384
  1. 槽位分配:這些槽位會均勻分布在不同的主節(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:profileuser:1000:settings不同槽位
  • {user:1000}:profile{user:1000}:settings相同槽位

應用場景: 保證相關數(shù)據(jù)存儲在同一節(jié)點上,支持多鍵操作(如 MGET {user:1000}:profile {user:1000}:settings

3.5 集群模式工作原理

  1. 節(jié)點發(fā)現(xiàn):通過 MEET 消息發(fā)現(xiàn)新節(jié)點
  2. 槽位分配:通過 CLUSTER ADDSLOTS 命令分配槽位
  3. 狀態(tài)同步:節(jié)點間通過 Gossip 協(xié)議交換集群狀態(tài)(IP、端口、運行ID、槽位信息)
  4. 請求路由
    • 節(jié)點存儲映射:每個節(jié)點都存儲完整的哈希槽與節(jié)點的對應關系
    • 客戶端緩存映射:客戶端也會緩存一份哈希槽映射關系,負責將請求直接發(fā)送到正確的節(jié)點
    • MOVED 重定向:當集群擴容/縮容導致客戶端緩存的舊映射失效,請求發(fā)送到錯誤的節(jié)點時,節(jié)點會返回 MOVED 重定向,客戶端據(jù)此更新映射關系并重新發(fā)送請求到正確的節(jié)點
  5. 故障檢測:通過 PING/PONG 消息檢測節(jié)點狀態(tài)
  6. 故障轉移:從節(jié)點檢測到主節(jié)點失效后發(fā)起選舉,成為新的主節(jié)點

3.6 客戶端類型

Redis 集群客戶端分為智能客戶端非智能客戶端兩種類型:

類型 特點 性能
非智能客戶端 不緩存槽位映射,每次請求可能需要重定向 較低,多次網(wǎng)絡往返
智能客戶端 緩存槽位映射,直接將請求發(fā)送到正確節(jié)點 較高,減少重定向次數(shù)

智能客戶端工作流程

  1. 初始化:啟動時從集群節(jié)點獲取完整的槽位映射關系
  2. 本地緩存:緩存哈希槽與節(jié)點的對應關系
  3. 直接路由:根據(jù)緩存的映射,直接將請求發(fā)送到正確的節(jié)點
  4. 自動更新:收到 MOVEDASK 響應時,自動更新本地緩存

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 的三種高可用模式適用于不同的場景:

  1. 主從模式:適用于數(shù)據(jù)量不大,只需要讀寫分離數(shù)據(jù)備份的場景。配置簡單,但主節(jié)點故障需要人工干預。

  2. 哨兵模式:在主從模式基礎上增加了自動故障轉移功能,適用于數(shù)據(jù)量不大但需要高可用的場景。缺點是不支持數(shù)據(jù)分片。

  3. 集群模式:通過數(shù)據(jù)分片實現(xiàn)水平擴展,同時具備高可用機制,適用于數(shù)據(jù)量大、需要分片和分片內高可用的現(xiàn)代分布式應用。

重要結論:

  • 哨兵模式和集群模式在主從復制的基本機制上是相似的,都基于 PSYNC2 協(xié)議,都支持全量和增量同步
  • 集群模式內置了高可用機制,不需要額外的哨兵組件
  • 選擇哪種模式主要取決于數(shù)據(jù)量大小、性能要求系統(tǒng)復雜度等因素

推薦:

  • 小規(guī)模應用(數(shù)據(jù)量 < 10GB):使用哨兵模式
  • 大規(guī)模應用(數(shù)據(jù)量 > 10GB):使用集群模式
  • 集群模式因為其內置的高可用性和自動主從管理機制,更適合現(xiàn)代分布式應用的需求。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容