分布式緩存:三萬字詳解Redis

文章目錄

  • 緩存全景圖
  • Pre
  • Redis 整體認知框架
    • 一、Redis 簡介
    • 二、核心特性
    • 三、性能模型
    • 四、持久化詳解
    • 五、復制與高可用
    • 六、集群與分片方案
  • Redis 核心數據類型
    • 概述
    • 1. String
    • 2. List
    • 3. Set
    • 4. Sorted Set(有序集合)
    • 5. Hash
    • 6. Bitmap
    • 7. Geo
    • 8. HyperLogLog
  • Redis 協議分析
    • 1. RESP 設計原則
    • 2. 三種響應模型與特殊模式
    • 3. 兩種請求格式
      • 3.1 Inline 命令格式
      • 3.2 Array(數組)格式
    • 4. 五種響應格式詳解
    • 5. 協議分類概覽
    • 6. Redis Client 選型與改進建議
  • Redis 的核心組件
    • 一、系統架構概覽
    • 二、事件處理機制
    • 三、數據管理
    • 四、功能擴展(Module System)
    • 五、系統擴展(Replication & Cluster)
  • Redis的事件驅動模型
    • 一、事件驅動模型概述
    • 二、文件事件處理詳解
      • 2.1 Reactor 模式四部分
      • 2.2 IO 多路復用的四種實現及選型邏輯
      • 2.3 aeProcessEvents:事件收集與派發流程
      • 2.4 三類文件事件處理函數
    • 三、時間事件機制剖析
  • Redis 協議解析及處理
    • 一、協議解析
    • 二、協議執行
  • Redis 內部數據結構
    • 一、RedisDb 結構
    • 二、redisObject 抽象
    • 三、dict 哈希表
    • 四、sds 簡單動態字符串
    • 五、壓縮列表(ziplist)
    • 六、快速列表(quicklist)
    • 七、跳躍表(zskiplist)
    • 八、數據類型與內部結構映射
  • Redis 淘汰策略
    • 一、淘汰原理
    • 二、淘汰方式
    • 三、淘汰策略與 Eviction Pool
    • 四、八種淘汰策略詳解
  • Redis 的三種持久化方案及崩潰后數據恢復流程
    • 一、RDB 持久化
    • 二、AOF 持久化
    • 三、混合持久化
  • Redis 后臺異步 IO(BIO)
    • 一、BIO 線程設計動機
    • 二、BIO 線程模型
    • 三、BIO 任務類型
    • 四、BIO 處理流程
  • Redis 多線程架構
    • 一、主線程職責
    • 二、IO 線程設計
    • 三、命令處理完整流程
    • 四、多線程方案優劣
  • 復制架構原理
    • 一、復制架構原理
    • 二、同步方式對比
    • 三、psync 與 psync2 優化
    • 四、復制連接與授權流程
    • 五、復制過程詳析
      • 5.1 增量同步流程
      • 5.2 全量同步流程
    • 六、注意事項
  • Redis 集群的分布式方案
    • 1. Client 端分區
      • 1.1 原理與哈希算法
      • 1.2 DNS 動態管理
      • 1.3 優缺點
    • 2. Proxy 分區方案
      • 2.1 架構概覽
      • 2.2 典型實現
      • 2.3 優缺點
    • 3. 原生 Redis Cluster
      • 3.1 Slot 與 Gossip 架構
      • 3.2 讀寫與重定向
      • 3.3 在線擴縮容與數據遷移
      • 3.4 優缺點
    • 4. 對比與選型建議

在這里插入圖片描述


緩存全景圖

在這里插入圖片描述


Pre

分布式緩存:緩存設計三大核心思想

分布式緩存:緩存的三種讀寫模式及分類

分布式緩存:緩存架構設計的“四步走”方法

分布式緩存:緩存設計中的 7 大經典問題_緩存失效、緩存穿透、緩存雪崩

分布式緩存:緩存設計中的 7 大經典問題_數據不一致與數據并發競爭

分布式緩存:緩存設計中的 7 大經典問題_Hot Key和Big Key

Redis 整體認知框架

在這里插入圖片描述

一、Redis 簡介

  • 實現與授權:Redis 基于 ANSI C 語言編寫,采用 BSD 許可,代碼輕量、易于嵌入。
  • 內存存儲:所有數據均保存在內存中,因此具有極低的讀寫延遲,可做緩存、數據庫、消息中間件等多種角色。
  • 多庫支持:Redis 即 Remote Dictionary Server,實例內部維護多個邏輯數據庫(默認為 16 個),通過 SELECT 命令切換操作目標。

二、核心特性

  1. 豐富的數據類型:除基本的字符串(String)外,Redis 還原生支持 List、Set、Sorted Set(ZSet)、Hash;以及 Bitmap、HyperLogLog、Geo 等特殊結構,一機多用。

  2. 雙重持久化

    • RDB 快照:定時或達到修改閾值時,將內存全量快照寫入 .rdb 文件,適合冷備份;
    • AOF 追加:將每條寫命令追加到 .aof 文件,可配置同步頻率,保障最小數據丟失。
      線上系統常用“RDB+ AOF 混合”策略:平時頻繁追加 AOF,低峰期觸發 BGSAVE 生成新快照;遇到 AOF 文件過大時,用 BGREWRITEAOF 重寫精簡。
  3. 讀寫分離:一主多從架構,將寫請求指向 Master,讀請求分發至多個 Slave,顯著提高讀吞吐。

  4. Lua 腳本與事務

    • Lua 腳本:從 Redis 2.6 起支持,腳本內多命令打包,可實現原子性操作并減少網絡往返;
    • 事務:通過 MULTI/EXEC 打包命令,確保命令序列原子執行,中途出錯則全部丟棄。
  5. 集群支持:Redis Cluster 原生實現分布式,基于 Slot 哈希機制,無中心節點,實現自動擴縮容與故障轉移。

三、性能模型

  • 單線程+事件驅動:網絡 IO 與命令處理均在主線程中完成,基于 epoll(或 kqueue、evport)無阻塞多路復用,避免鎖競爭與上下文切換。

  • 高 QPS:單實例可輕松突破 100k QPS,得益于純內存操作與無鎖設計。

  • 后臺子進程/線程

    • BGSAVE/BGREWRITEAOF/全量復制:主進程遇到重負荷持久化或復制任務時,fork 子進程執 行,主進程繼續提供服務;

    • BIO 線程池:三個后臺線程負責文件關閉、AOF 緩沖刷盤、對象釋放,進一步減輕主線程壓力。

在這里插入圖片描述

四、持久化詳解

在這里插入圖片描述

  • RDB:快速生成緊湊快照,恢復速度快;適合冷備份,但數據持久性依賴觸發頻率。
  • AOF:按命令追加,能做到每秒或每次寫入同步,重放日志恢復更完整;但文件體積隨命令量增長,需定期重寫。
  • 混合策略:推薦生產環境開啟 AOF 并定期重寫,同時在低峰期執行 RDB 快照,以兼顧恢復速度與數據完整性。

五、復制與高可用

  • 全量同步:Slave 首次連接或復制緩沖不足時,Master fork 子進程生成 RDB 快照并傳輸,Slave 接收后加載;
  • 增量復制:Slave 重連且累積命令量在緩沖區可承載范圍內時,僅傳輸缺失命令,降低復制開銷。
  • 故障切換:當 Master 宕機,可手動或通過哨兵(Sentinel)將任意 Slave 提升為 Master,保障業務連續性。

六、集群與分片方案

  1. Client 分片:客戶端根據一致性哈希或取模自行路由到不同實例,簡單但擴縮容麻煩;
  2. Proxy 層:如 Twemproxy,在前端做路由與健康檢查,后端實際節點變動只需更新 Proxy 配置;
  3. Redis Cluster:官方原生集群,使用 16384 個 Slot 管理鍵空間,支持在線遷移、故障轉移與自動均衡。

Redis 核心數據類型

概述

Redis 共支持以下 及 種核心數據類型:

  1. String:二進制安全的字符串類型
  2. List:按插入順序排列的雙向鏈表
  3. Set:無序且元素唯一的集合
  4. Sorted Set(ZSet):帶分值的有序集合
  5. Hash:字段–值映射表
  6. Bitmap:基于 String 的位圖封裝
  7. Geo:地理位置類型,基于 ZSet 實現
  8. HyperLogLog:基數統計的近似算法

1. String

  • 存儲方式

    • 小于 1 MB 時,采用 raw encoding,預分配兩倍長度來減少頻繁擴容;
    • 超過 1 MB 時,每次額外預分配 1 MB。
  • 整型編碼:對于純數字字符串,使用整型編碼,以節省內存并加速算術運算。

  • 常用指令SETGETMSETINCRDECR 等。

  • 典型場景

    • 緩存普通文本、序列化對象;

    • 計數器(PV、UV、限流);

    • 分布式鎖的簡單實現。

      SET user:1001:token "abcd1234"
      INCR page:views
      

2. List

  • 底層實現:快速雙向鏈表,支持頭尾 O(1) 插入/彈出。

  • 指令摘要

    • 插入:LPUSHRPUSHLINSERT
    • 彈出:LPOPRPOP、阻塞式 BLPOPBRPOP
    • 范圍查詢:LRANGE(支持負索引)
  • 時間復雜度:對頭/尾操作為 O(1),隨機訪問或插入為 O(N)。

  • 典型場景

    • 消息隊列(工作隊列、發布/訂閱前端緩沖);

    • Feed Timeline(用戶動態按時間順序追加);

    • 簡易棧/隊列。

      RPUSH queue:tasks task1 task2
      BLPOP queue:tasks 0   # 阻塞直到有新任務
      LRANGE queue:tasks 0 9  # 獲取前 10 個元素
      

3. Set

  • 底層實現:哈希表,保證元素唯一且無序。

  • 指令摘要SADDSREMSISMEMBERSDIFFSINTERSUNIONSPOPSRANDMEMBER

  • 時間復雜度:插入、刪除、查找均為 O(1)。

  • 典型場景

    • 好友關注列表、互關判斷;

    • 推薦系統中的離線/在線標簽去重;

    • 來源 IP 白名單/黑名單。

      SADD user:1001:friends 1002 1003
      SISMEMBER user:1001:friends 1003  # 返回 1
      

4. Sorted Set(有序集合)

  • 底層實現:跳表 + 哈希,按分值升序排列。

  • 指令摘要ZADDZREMZSCOREZRANGEZINCRBYZINTERSTOREZUNIONSTORE

  • 特點:元素唯一,分值可重復;快速算分與排名。

  • 典型場景

    • 實時排行榜(游戲分數、熱度榜單);

    • 按權重排序的數據展示;

    • 定時任務系統(利用分值表示時間戳)。

      ZADD leaderboard 100 user:1001
      ZRANGE leaderboard 0 9 WITHSCORES  # TOP10
      

5. Hash

  • 底層實現:field–value 映射,內部也是哈希表。

  • 指令摘要HSET/HMSETHGET/HMGETHEXISTSHINCRBYHGETALL

  • 時間復雜度:單 field 操作為 O(1)。

  • 典型場景

    • 存儲對象屬性,如用戶資料、商品信息;

    • 實現類似關系型數據庫表的一行;

    • 業務統計字段聚合。

      HMSET user:1001 name "Alice" age 30
      HINCRBY user:1001:stats login_count 1
      

6. Bitmap

  • 底層實現:基于 String 的位操作。

  • 指令摘要SETBITGETBITBITCOUNTBITOPBITFIELDBITPOS

  • 特點:按位存儲,內存占用極低;位運算高效。

  • 典型場景

    • 用戶活躍打卡(N 天登錄);

    • 標簽屬性存儲與多維統計;

    • 簡易布隆過濾器原型。

      SETBIT login:20250525 1001 1
      BITCOUNT login:20250525  # 當天活躍用戶數
      

7. Geo

  • 底層實現:封裝于 Sorted Set,通過 GeoHash 將經緯度映射為分值。

  • 指令摘要GEOADDGEOPOSGEODISTGEORADIUSGEORADIUSBYMEMBER

  • 特點:支持范圍查詢與距離計算。

  • 典型場景

    • 附近的人/店鋪/車輛搜索;

    • 地理圍欄告警;

    • 實時位置服務(LBS)。

      GEOADD restaurants 116.397128 39.916527 "PekingDuck"
      GEORADIUS restaurants 116.40 39.92 5 km WITHDIST
      

8. HyperLogLog

  • 底層實現:近似基數統計算法,稀疏與稠密兩種存儲,自適應切換。

  • 指令摘要PFADDPFCOUNTPFMERGE

  • 特點:固定 ≈12KB 內存;誤差率 ≈0.81%。

  • 典型場景

    • 大規模 UV 統計;

    • 海量搜索詞匯去重;

    • 日志中的獨立源 IP 計數。

      PFADD uv:202505 user:1001 user:1002
      PFCOUNT uv:202505  # 當月獨立訪客數(近似)
      

Redis 協議分析

在這里插入圖片描述


1. RESP 設計原則

Redis 序列化協議 RESP 的設計堅持三條原則:

  1. 實現簡單:協議格式直觀,便于不同語言的客戶端快速實現。
  2. 可快速解析:結構清晰、前綴標記,使得解析器能夠以最低開銷完成讀寫。
  3. 便于閱讀:即便用 Telnet 交互,也能通過簡單的符號輕松定位請求與響應邊界。

2. 三種響應模型與特殊模式

Redis 默認使用“Ping-Pong”模型:客戶端發起一個請求,服務端立即返回一個響應,實現一問一答。
此外還有兩種特殊模式:

  • Pipeline 模式:客戶端一次性發送多條命令,不等待中間響應,待全部發送完后再按序接收服務端響應,減少網絡往返。
  • Pub/Sub 模式:客戶端通過 SUBSCRIBE 進入訂閱狀態,此后無需再次發起請求,即可持續接收服務端基于頻道推送的消息;除訂閱相關命令,其他命令均失效。

3. 兩種請求格式

3.1 Inline 命令格式

適用于交互式會話(如 Telnet),命令與參數以空格分隔,結尾以 \r\n

mget key1 key2\r\n

3.2 Array(數組)格式

更規范的二進制安全格式,也是生產環境客戶端默認使用:

*3\r\n$4\r\nMGET\r\n$4\r\nkey1\r\n$4\r\nkey2\r\n

其中 *3 表示數組長度為 3,每個元素前以 $<字節數> 聲明。


4. 五種響應格式詳解

Redis 響應客戶端請求時,基于 RESP 定義了 5 類格式:

  1. Simple String(簡單字符串)

    • 前綴 +,不可包含 \r\n,以 \r\n 結束。
    • 用于返回 OK、PONG 等簡短狀態。
    +OK\r\n
    
  2. Error(錯誤)

    • 前綴 -,后跟錯誤類型(ERR/WRONGTYPE 等)及描述,以 \r\n 結束。
    -ERR unknown command 'foo'\r\n
    
  3. Integer(整數)

    • 前綴 :,后跟整數字符串,以 \r\n 結束。
    • 代表計數、長度或布爾(0/1)等。
    :1000\r\n
    
  4. Bulk String(字符串塊)

    • 前綴 $,后跟內容字節長度,再 \r\n;隨后是真實內容,再 \r\n
    • 支持二進制安全,最大可達 512MB。
    $6\r\nfoobar\r\n
    
    • 空字符串:$0\r\n\r\n;NULL:$-1\r\n
  5. Array(數組)

    • 前綴 *,后跟元素個數,再 \r\n;隨后依次是各元素(可嵌套上述任何格式)。
    *2\r\n$3\r\nGET\r\n$3\r\nkey\r\n
    
    • 空數組:*0\r\n;NULL 數組:*-1\r\n

5. 協議分類概覽

除了與 8 種數據結構直接對應的命令協議,Redis 還定義了以下 8 類協議:

  1. Pub/Sub 協議SUBSCRIBE/PUBLISH
  2. 事務協議MULTI/EXEC/DISCARD
  3. 腳本協議EVAL/EVALSHA/SCRIPT
  4. 連接協議AUTH/SELECT/QUIT
  5. 復制協議REPLICAOF/PSYNC/ROLE
  6. 配置協議CONFIG GET/CONFIG SET
  7. 調試統計協議INFO/MONITOR/SLOWLOG
  8. 內部命令MIGRATE/DUMP/RESTORE

6. Redis Client 選型與改進建議

以 Java 為例,目前主流客戶端有:

  • Jedis:輕量、直觀,支持連接池,幾乎覆蓋所有命令,但原生不支持讀寫分離。
  • Redisson:基于 Netty 的非阻塞 IO,支持異步調用、讀寫分離、負載均衡及 Spring Session 集成,但實現較為復雜。
  • Lettuce:也是基于 Netty,完全非阻塞、線程安全,可在多線程環境中共享同一連接;提供同步、異步(Future)、響應式(Reactive Streams)和 RxJava 風格的多種調用方式;原生支持 Redis Cluster、Sentinel、讀寫分離,自動故障轉移;客戶端實現簡潔,依賴少,適合高并發、低延遲場景。

改進建議

  • 在異常訪問時實現重試與熔斷;
  • 動態感知主從切換,自動調整連接;
  • 多 Slave 場景下添加負載均衡策略;
  • 與配置中心和集群管理平臺集成,實現實時路由和高可用。

Redis 的核心組件

一、系統架構概覽

Redis 的核心組件主要包括以下四大模塊:

  • 事件處理(Event Loop):基于作者開發的 ae 事件驅動模型,實現高效網絡 IO 和定時任務調度
  • 數據存儲與管理:內存數據庫 redisDB,支持多庫、多數據類型、多底層結構
  • 功能擴展(Module System):可插拔模塊化設計,無需改動核心即可引入新數據類型與命令
  • 系統擴展(Replication & Cluster):主從復制與 Cluster 分片,滿足高可用與橫向擴容需求

二、事件處理機制

在這里插入圖片描述

  1. ae 事件驅動模型概述

    • 封裝 select/epoll/kqueue/evport,實現 IO 多路復用
    • 監聽多個 socket,把網絡讀寫、命令執行、定時任務整合到同一個循環
  2. 客戶端連接管理

    • 收到新連接時,創建 client 結構體,維護狀態、讀寫緩沖
    • 請求到達后將命令讀取到緩沖區,并解析成參數列表
  3. 命令處理流程

    • 根據命令名稱映射到 redisCommand
    • 對參數進行進一步解析與校驗
    • 執行命令對應的處理函數
  4. 時間事件(Time Events)

    • 周期性執行 serverCron:包括統計更新、過期鍵清理、AOF/RDB 持久化觸發等

三、數據管理

  1. 內存數據庫結構

    • 每個邏輯庫對應一個 redisDB 結構,內部通過 dict 存儲 key/value
    • 八種數據類型(String、List、Set、Hash、ZSet、Stream、Bitmap、HyperLogLog)各自采用一或多種底層結構
  2. 持久化策略

    • AOF(Append Only File):將每次寫操作追加到緩沖,按策略刷盤
    • RDB(Redis DataBase Snapshot):定期將全量數據快照落地,生成緊湊的二進制文件
  3. 線程模型與非阻塞

    • 核心線程為單線程,避免任何內核阻塞
    • BIO 線程池:專門處理可能阻塞的文件 close、fsync 等操作,保證主線程性能
  4. 內存淘汰與過期

    • 過期鍵及時清理,空閑掃描或惰性刪除相結合
    • 八種淘汰策略(如 LRU、LFU、TTL 優先等),結合 eviction pool 高效回收內存

四、功能擴展(Module System)

  • 模塊加載:動態鏈接庫,可在啟動時或運行時加載/卸載

  • API 接口

    • RedisModule_Init:初始化模塊
    • RedisModule_CreateCommand:注冊新命令
  • 應用場景:自定義數據結構、高級功能(例如圖數據庫、機器學習推理)


五、系統擴展(Replication & Cluster)

  1. 主從復制(Replication)

    • 支持全量同步與增量復制
    • Slave 重連、主從切換后均可繼續增量復制,提升可用性
    • 讀寫分離:將讀請求分攤到多個節點,減輕主節點負載
  2. 分片集群(Cluster)

    • 16384 個 slot,按 Hash 分布到不同節點
    • 客戶端計算 slot,根據 slot 定位節點
    • 錯誤節點自動重定向(MOVED/ASK)
    • 在線擴容:通過遷移 slot 實現節點增減

Redis的事件驅動模型

一、事件驅動模型概述

Redis 作為一個高性能的內存數據庫,充分利用事件驅動模式來處理幾乎所有核心操作。與 Memcached 依賴 libevent/ libev 不同,Redis 作者從零開始,開發了自研的事件循環組件,封裝在 aeEventLoop 及相關結構體中。這樣做的動機是:

  • 最小化外部依賴:減少因第三方庫升級或兼容性帶來的不確定性;
  • 輕量可控:自研實現更契合 Redis 的業務場景,代碼更簡潔,性能更容易優化;
  • 靈活擴展:可在事件模型中無縫接入文件事件與時間事件的統一調度。

在這里插入圖片描述

Redis 的事件驅動模型主要處理兩類事件:

  1. 文件事件:與 socket 讀寫、連接建立/關閉直接相關的 IO 事件;
  2. 時間事件:周期性或單次需要在指定時間點執行的任務,例如定期統計、Key 淘汰、緩沖寫出等。

二、文件事件處理詳解

Redis 在文件事件處理上采用經典的Reactor 模式,將整個流程拆分為四部分:連接 socket、IO 多路復用、文件事件分派器與事件處理器。

在這里插入圖片描述

2.1 Reactor 模式四部分

  1. 連接 Socket:監聽客戶端連接的 TCP 端口與已建立連接的客戶端 Socket;
  2. IO 多路復用:通過底層操作系統接口同時監控多個描述符的可讀寫狀態;
  3. 文件事件分派器:調用 aeProcessEvents,從多路復用層獲取觸發的事件;
  4. 事件處理器:根據事件類型(可讀/可寫)調用注冊好的回調函數執行實際邏輯。

2.2 IO 多路復用的四種實現及選型邏輯

Redis 封裝了四種主流的多路復用方案,編譯時按優先級自動選擇:

  • evport(Solaris 專有)
  • epoll(Linux 最佳選擇)
  • kqueue(大多數 BSD 系統)
  • select(通用但性能最低)

前三者直接調用內核機制,能同時服務數十萬文件描述符;select 則每次需掃描全部描述符,時間復雜度 O(n),且受描述符數量上限(默認 1024/2048)限制,不適合線上高并發場景。對應實現分布在 ae_evport.cae_epoll.cae_kqueue.cae_select.c 四個代碼文件中。

2.3 aeProcessEvents:事件收集與派發流程

aeProcessEvents 是 Redis 文件事件的核心分派器,執行流程大致如下:

  1. 計算下一次阻塞等待的超時時間(兼顧時間事件);
  2. 調用 aeApiPoll(內置封裝)阻塞或非阻塞等待文件事件;
  3. 收集觸發的事件,將它們封裝到 aeFiredEvents 數組中,每項記錄文件描述符與事件類型;
  4. 將底層事件類型(如 EPOLLIN/EPOLLOUT/EPOLLERR)映射為 Redis 事件標志(AE_READABLE/AE_WRITABLE);
  5. 依次遍歷 aeFiredEvents先讀后寫地 dispatch 到注冊在 aeEventLoop 中的具體事件處理器。

2.4 三類文件事件處理函數

Redis 對文件事件的注冊與處理主要分為:

  1. 連接處理acceptTcpHandler

    • initServer 階段注冊監聽 socket 的讀事件;
    • 有新連接時,接受連接、創建 client 結構,獲取遠端 IP/端口;
    • 單次循環最多處理 1000 個新連接請求;
  2. 請求讀取readQueryFromClient

    • 為每個 client socket 注冊讀事件;
    • 讀取客戶端發來的命令數據,填充到 client->query_buf
    • 按 inline 或 multibulk 格式解析命令,校驗參數及當前實例狀態后,執行對應的 redisCommand
    • 將執行結果寫入 client->reply_buf
  3. 回復發送sendReplyToClient

    • 在命令執行完將結果放入寫緩沖后,注冊寫事件;
    • 當 socket 可寫時,將緩沖區數據發送給客戶端。
      在這里插入圖片描述

三、時間事件機制剖析

與文件事件并行,Redis 的時間事件在同一個 aeEventLoop 內作為鏈表管理。每個時間事件包含五個核心屬性:

  • 事件 ID:全局唯一自增;
  • 執行時間when_secwhen_ms,精確到毫秒;
  • 處理器timeProc 函數指針;
  • 關聯數據clientData 傳遞給處理器使用;
  • 雙向鏈表指針prevnext,便于插入與遍歷。

時間事件分為:

  • 單次事件:執行一次后即標記刪除;
  • 周期事件:執行后更新下一次執行時間,保持循環。

aeProcessEvents 中,文件事件處理前后都會遍歷一次時間事件鏈表,執行所有到期的事件:

  1. 逐一比較事件時間與當前時鐘;
  2. 對可執行事件調用 timeProc(clientData)
  3. 若周期事件,更新 when_sec/when_ms;若單次事件,標記 id=-1,下一輪清除。

Redis 默認主要的時間事件包括:

  • serverCron:定期執行統計、淘汰、維護緩沖等任務;
  • moduleTimerHandler:模塊化擴展的定時回調。

Redis 協議解析及處理

當事件循環檢測到客戶端有請求到來時,Redis 如何從網絡讀入原始數據、解析成命令與參數,最終執行并返回結果。


一、協議解析

在這里插入圖片描述

  1. 讀取請求到 Query Buffer

    • 當 socket 可讀事件觸發,Redis 會調用 readQueryFromClient,從客戶端連接的文件描述符讀取數據到 client->querybuf
    • 默認讀緩沖大小為 16 KB;若單次請求長度超過 1 GB,Redis 會報錯并關閉連接,防止惡意或異常請求耗盡內存。
  2. 判斷協議類型:MULTIBULK vs INLINE

    • MULTIBULK(以 * 開頭)

      • 首字節為 *,表示后續是一個塊數組。格式為:

        *<參數個數>\r\n
        $<第1個參數字節數>\r\n
        <參數1內容>\r\n
        …  
        
      • 逐行讀取,根據 $ 指示讀取固定長度數據,直到完整填充所有參數。

    • INLINE(單行字符串)

      • 首字節非 *,整個請求以 \r\n 結尾。命令和參數用空格分隔:

        set mykey hello\r\n
        
      • Redis 會將整行切分,再按空格拆分命令及參數。

  3. 填充 client->argcclient->argv

    • 解析結束后,將參數個數寫入 client->argc
    • 對于每個參數,創建一個 robj(Redis 對象),存入 client->argv 數組,以便后續命令執行使用。

二、協議執行

在這里插入圖片描述

Redis 協議解析及處理

  1. 處理特殊命令:QUIT

    • argv[0]quit,Redis 直接返回 +OK\r\n 并將 CLIENT_CLOSE_AFTER_REPLY 標記置位,表示回復后關閉連接。
  2. 查找并執行命令

    • 使用 lookupCommand 在全局命令表(server.commands)中查找 argv[0] 對應的 redisCommand 結構。

    • 若找不到,則調用 addReplyErrorFormat(c,"ERR unknown command '%s'",c->argv[0]->ptr),向客戶端返回未知命令錯誤。

    • 找到后,進入命令執行階段:

      c->cmd = cmd;
      c->cmd->proc(c);
      
      • proc 是命令對應的函數指針,如 setCommandgetCommand 等。
  3. 寫入響應與副作用

    • 命令執行完成后,依照命令邏輯通過 addReply* 系列函數將響應數據寫入 client->buf(寫緩沖區)。

    • 若該命令為寫操作,且開啟了 AOF 或者當前角色為主節點,還需將寫命令推送給 AOF 線程與所有從節點:

      • 調用 feedAppendOnlyFile(c, ...);
      • 調用 replicationFeedSlaves(c, ...);
    • 同時,更新命令統計,如 server.stat_numcommands++


Redis 內部數據結構

Redis 的內存數據結構層——Redis 如何在內存中組織和管理各種對象,才能在單線程模型下實現高性能與高擴展性。

在這里插入圖片描述


一、RedisDb 結構

在這里插入圖片描述

  • 多庫支持:每個實例默認可配置 16 個邏輯庫(db0db15),通過命令 SELECT $dbID 切換。

  • 核心字典

    • dict(主字典):存儲 key → value 映射
    • expires:存儲 key → 過期時間
  • 非核心字典

在這里插入圖片描述

  • blocking_keys:記錄 BLPOP/BRPOP 等阻塞列表的 key → client 列表
  • ready_keys:當元素入隊觸發喚醒時,將 key 加入此字典與全局 server.read_keys 列表中
  • watched_keys:用于事務 WATCH 監控的 key → client 列表

二、redisObject 抽象

在這里插入圖片描述

Redis 中任何存儲的值都封裝為 redisObject,包含五個核心字段:

  1. type:對象類型(OBJ_STRINGOBJ_LISTOBJ_SETOBJ_ZSETOBJ_HASHOBJ_MODULEOBJ_STREAM
  2. encoding:底層編碼(如 RAWINTHTZIPLIST 等)
  3. LRU:用于 LRU/LFU 淘汰策略的訪問記錄
  4. refcount:引用計數,支持對象共享與內存自動回收
  5. ptr:指向具體底層數據結構(如 sdsdictziplistquicklistzskiplist 等)

三、dict 哈希表

在這里插入圖片描述

  • 雙表設計dict 結構內維護長度為 2 的哈希表數組 ht[0]ht[1]

  • 漸進式 rehash

    1. ht[0] 裝載因子超閾值,分配 ht[1](容量為 ht[0] 的兩倍)
    2. 每次哈希操作順帶遷移部分桶,使用 rehashidx 記錄遷移進度
  • 沖突解決:每個桶為 dictEntry 的單向鏈表
    在這里插入圖片描述

  • 靈活可擴展dict 可用于主字典、過期字典,也可作為 Set、Hash 類型的內部存儲


四、sds 簡單動態字符串

在這里插入圖片描述

  • 基本結構:底層為 sdshdr + char buf[]

    • len:當前字符串長度
    • alloc:已分配空間大小
    • flags:類型與子類型標志
    • buf:字符數據(二進制安全,允許包含 \0
      在這里插入圖片描述
  • 多種子類型(從 Redis 3.2 起)

    • sdshdr5:極短字符串,僅 flagsbuf
    • sdshdr8/16/32/64:根據長度選擇合適的整型字段,節省內存
  • 優勢

    • O(1) 獲取長度,無需遍歷
    • 動態擴展與收縮,二進制安全

五、壓縮列表(ziplist)

為了節約內存,并減少內存碎片,Redis 設計了 ziplist 壓縮列表內部數據結構。壓縮列表是一塊連續的內存空間,可以連續存儲多個元素,沒有冗余空間,是一種連續內存數據塊組成的順序型內存結構。

在這里插入圖片描述

  • 連續內存布局,減少指針開銷與碎片

  • 結構字段

    1. zlbytes:總字節數
    2. zltail:尾節點距起始偏移
    3. zllen:節點數量
    4. entry…entry…:各節點數據
    5. zlend:結束標志(255)
  • 節點格式

    • 前驅長度、編碼長度、實際數據長度、編碼類型、數據
  • 適用場景

    • 小型 Hash(默認 ≤512 項、值 ≤64B)
    • 小型 ZSet(默認 ≤128 項、值 ≤64B)

在這里插入圖片描述
在這里插入圖片描述


六、快速列表(quicklist)

Redis 在 3.2 版本之后引入 quicklist,用以替換 linkedlist。因為 linkedlist 每個節點有前后指針,要占用 16 字節,而且每個節點獨立分配內存,很容易加劇內存的碎片化。而 ziplist 由于緊湊型存儲,增加元素需要 realloc,刪除元素需要內存拷貝,天然不適合元素太多、value 太大的存儲。

在這里插入圖片描述

  • 設計目標:結合 ziplist 的緊湊與 linkedlist 的靈活

  • 結構

    • 雙向鏈表節點 quicklistNode,每節點包含一個 ziplist
    • headtail 指針;count(總元素數);len(節點數);compress(LZF 壓縮深度)
  • 優點

    • 頭尾操作 O(1)
    • 避免過多內存碎片
    • 支持中間位置操作(O(n))

七、跳躍表(zskiplist)

跳躍表 zskiplist 是一種有序數據結構,它通過在每個節點維持多個指向其他節點的指針,從而可以加速訪問。跳躍表支持平均 O(logN) 和最差 O(n) 復雜度的節點查找。在大部分場景,跳躍表的效率和平衡樹接近,但跳躍表的實現比平衡樹要簡單,所以不少程序都用跳躍表來替換平衡樹。
在這里插入圖片描述

  • 多級索引:在每個節點維護多層前進指針與跨度,近似平衡樹性能

  • 結構

    • zskiplistheadertaillengthlevel
    • zskiplistNodeele(sds)、scorebackward、多級 level[i]forward + span
  • 性能

    • 平均 O(log N) 查找/插入/刪除
    • 同分數元素按字典序排序
  • 適用場景

    • 大型 Sorted Set、Geo 類型(超出 ziplist 閾值)

八、數據類型與內部結構映射

數據類型內部存儲結構
Stringsds / 整數對象
Listquicklist
Setdict
Hashziplist(小型)/ dict(大型)
Sorted Setziplist(小型)/ zskiplist(大型)
Streamradix tree + listpacks
HyperLogLogsds
Bitmapsds
Geoziplist(小型)/ zskiplist(大型)

Redis 淘汰策略

當 Redis 內存到達或超過 maxmemory 限制時,系統如何精確、高效地清理無用或不活躍的數據,保障緩存的命中率與訪問性能。

在這里插入圖片描述


一、淘汰原理

  1. 內存閾值與觸發條件

    • 通過配置 maxmemory 設置 Redis 可用的最大內存。
    • 當內存使用量超過閾值,或在定期過期檢查時發現過期 key,均觸發淘汰動作。
  2. 場景一:定期過期掃描(serverCron)

    • 周期性執行 serverCron,對每個 redisDbexpires 過期字典進行采樣:

      1. 隨機取 20 個帶過期時間的 key 樣本;
      2. 若其中超過 5 個已過期(比例 >25%),繼續取樣并清理,直至過期比例 ≤25% 或 時間耗盡;
    • 若某 DB 的過期字典填充率 <1%,則跳過采樣。

    • 為避免阻塞主線程,清理時限:

      • Redis 5.0 及之前:慢循環策略,默認 25ms;
      • Redis 6.0:快循環策略,限時 1ms。
  3. 場景二:命令執行時檢查

    • 在每次執行命令前,檢查當前內存占用是否已超限;
    • 若超限,則立即依據所選 maxmemory-policy 進行 key 淘汰,釋放內存后再繼續執行寫命令。

二、淘汰方式

Redis 提供兩種刪除方式,以平衡主線程響應和內存回收的及時性:

  1. 同步刪除

    • 直接在主線程中刪除 key 及其 value,并同步回收內存;
    • 適用于簡單值或復合類型元素數 ≤64 的情況。
  2. 異步刪除(Lazy Free)

    • 依賴 BIO 線程池異步回收內存,避免主線程因大對象刪除而阻塞;

    • 觸發條件:

      • lazyfree-lazy-expire:延遲過期清理;
      • lazyfree-lazy-eviction:延遲淘汰時;
    • 對象類型:list、set、hash、zset 中,元素數 >64 時使用。


三、淘汰策略與 Eviction Pool

為在維持高性能的同時,盡可能剔除最“冷”的數據,Redis 在淘汰前會:

  1. 隨機采樣 N 個 key(默認為 5)

  2. 計算每個樣本的“Idle”值

    • 對于 LRU,用空閑時間;LFU 則用 255 – 頻率;TTL 策略以 UINT_MAX – 過期時間
  3. 維護大小為 N 的 Eviction Pool

    • 按 Idle 從小到大插入,始終保留 Idle 最大的樣本;
  4. 最終剔除 Pool 中 Idle 最大的 key


四、八種淘汰策略詳解

策略名稱作用范圍算法原理適用場景
noeviction不淘汰任何 key達到內存上限后,對寫命令返回錯誤,讀命令正常小規模數據,Redis 作為持久存儲而非緩存
volatile-lru帶過期時間的 key基于 LRU,從 expires 中隨機 N 樣本,剔除空閑時間最長的 key熱點數據明顯,且淘汰對象均已設置過期時間的緩存場景
volatile-lfu帶過期時間的 key基于 LFU,從 expires 隨機 N 樣本,剔除使用頻率最低的 key(Idle = 255–freq)訪問頻率具有明顯冷熱區分的業務,且僅淘汰已設置過期時間的對象
volatile-ttl帶過期時間的 key剔除最近到期的 key(Idle = UINT_MAX–TTL)按剩余生命周期冷熱分區,優先清理即將過期的數據
volatile-random帶過期時間的 keyexpires 隨機選一個 key 直接剔除無明顯訪問熱點,且僅對帶過期時間的對象進行隨機清理
allkeys-lru所有 keyvolatile-lru 類似,但樣本來自主字典 dict全局范圍的 LRU 淘汰,適合全量緩存且有熱點區分的場景
allkeys-lfu所有 keyvolatile-lfu 類似,樣本來自主字典 dict訪問頻率冷熱明顯,需要對所有 key 進行頻率淘汰的場景
allkeys-random所有 key從主字典 dict 隨機選一個 key 直接剔除隨機訪問場景,無明顯熱點,全局隨機淘汰

Redis 的三種持久化方案及崩潰后數據恢復流程

Redis 持久化是一個將內存數據轉儲到磁盤的過程。Redis 目前支持 RDB、AOF,以及混合存儲三種模式。

在這里插入圖片描述

一、RDB 持久化

  1. 原理概述

    • 以快照方式將內存全量數據序列化為二進制格式,包含:過期時間、數據類型、key 與 value;
    • 重啟時(appendonly 關閉),直接加載 RDB 文件恢復數據。
  2. 觸發場景

    • 手動執行 SAVE(阻塞主進程)或 BGSAVE(子進程異步);
    • 配置 save <秒> <次數>:在指定時間內寫操作次數達到閾值自動觸發;
    • 主從復制全量同步時,主庫為了生成同步快照會執行 BGSAVE
    • 執行 FLUSHALL 或優雅 SHUTDOWN 時,自動觸發快照。

在這里插入圖片描述

  1. RDB 文件結構

    1. 頭部:版本信息、Redis 版本、生成時間、內存占用等;
    2. 數據區:按 DBID 分塊,依次寫入每個 redisDb 的主字典與過期字典條目,記錄過期時間及 LRU/LFU 元數據;
    3. 尾部:Lua 腳本等附加信息、EOF 標記(255)、校驗和(cksum)。
  2. 優缺點

    • 優點:文件緊湊、加載快;
    • 缺點:全量快照只能反映觸發時刻數據,之后變更丟失;子進程構建仍消耗 CPU,不能頻繁在高峰期執行;格式二進制,可讀性差,跨版本兼容性需謹慎。

二、AOF 持久化

在這里插入圖片描述

  1. 原理概述

    • 將每條寫命令以 Redis 協議的 MULTIBULK 格式追加到 AOF 文件;
    • 重啟時,按序加載并重放寫命令,恢復到最近狀態。
  2. 落地流程

在這里插入圖片描述

  1. 寫命令執行后寫入 AOF 緩沖;
  2. serverCron 周期將緩沖寫入文件系統緩沖;
  3. appendfsync 策略 fsync 同步到磁盤。

在這里插入圖片描述

  1. 同步策略

    • no:不主動 fsync,依賴操作系統(約 30s 同步),風險大;
    • always:每次寫緩沖后都 fsync,最安全但性能和磁盤壽命受影響;
    • everysec:每秒一次異步 fsync(BIO 線程),在安全性與性能間折中。
  2. AOF 重寫(Rewrite)

    • 通過 BGREWRITEAOF 或自動觸發,fork 子進程生成精簡命令集:

      1. 子進程掃描每個 redisDb,將內存快照轉為寫命令寫入臨時文件;
      2. 主進程繼續處理請求,并將寫命令同時寫入舊 AOF 與 rewrite 緩沖;
      3. 子進程完成后,主進程合并 rewrite 緩沖并替換舊文件;舊文件由 BIO 異步關閉。

在這里插入圖片描述

  1. 優缺點

    • 優點:記錄所有寫操作,最多丟失 1–2 秒;兼容性好、可讀;
    • 缺點:文件隨時間增大,包含大量中間狀態;恢復時需重放命令,速度相對較慢。

三、混合持久化

Redis 持久化是一個將內存數據轉儲到磁盤的過程。Redis 目前支持 RDB、AOF,以及混合存儲三種模式。

  1. 原理與配置

    • 自 Redis 4.0 引入,5.0 默認開啟;配置 aof-use-rdb-preamble yes
    • BGREWRITEAOF 時,子進程先將全量內存數據以 RDB 格式寫入 AOF 臨時文件,再追加期間新增寫命令;
  2. 流程

    1. fork 子進程;
    2. 子進程將內存快照寫為 RDB 格式到臨時文件;
    3. 追加子進程運行期間主進程緩沖的寫命令;
    4. 通知主進程替換 AOF,舊文件異步關閉。
  3. 優缺點

    • 優點:兼具 RDB 加載快與 AOF 新數據保留特性;恢復速度快且幾乎無數據丟失;
    • 缺點:頭部 RDB 部分依然為二進制,不易閱讀;跨版本兼容需測試。

  • RDB:全量快照,文件小、加載快,但存在數據丟失窗口;
  • AOF:命令追加,幾乎無丟失,兼容性高,但文件大、重放慢;
  • 混合:RDB+AOF,一體化折中方案。

Redis 后臺異步 IO(BIO)

Redis 核心線程單線程模型雖能高效處理多數操作,但對文件關閉、磁盤同步、以及大對象的逐一回收等系統調用依然容易導致短時阻塞,從而影響整體吞吐和響應延遲。接下來深入介紹 Redis 如何通過后臺 IO(BIO)線程,將這些“慢任務”異步化,保證主線程的高可用性與低延遲。

在這里插入圖片描述


一、BIO 線程設計動機

  • 單線程模型的挑戰

    • 主線程需處理所有客戶端請求、過期清理、淘汰等,性能極高;
    • 若再執行如 close()fsync()、大對象釋放等系統調用,短則數毫秒、長則上百毫秒,都將阻塞請求處理,造成卡頓;
  • 異步化解決思路

    • 將這些“慢任務”提交給后臺線程異步執行;
    • 主線程僅需快速入隊并繼續服務,顯著降低響應延遲波動。

二、BIO 線程模型

Redis 采用經典的生產者-消費者模式:

  • 生產者:主線程在檢測到慢任務時,構建相應的 BIO 任務結構并入隊;
  • 消費者:專屬的 BIO 線程阻塞等待隊列中的新任務,一旦被喚醒即取出并執行;
  • 同步機制:使用互斥鎖保護隊列,條件變量實現高效喚醒/等待,確保線程安全與低開銷。

三、BIO 任務類型

Redis 啟動時,為三類任務分別創建獨立的任務隊列與線程:

BIO 線程名稱任務隊列主要用途
closecloseQ關閉舊 AOF/客戶端/其他文件描述符,避免主線程被 close() 阻塞
fsyncfsyncQ將內核文件緩沖區的內容強制同步到磁盤(fsync()),保障數據持久化
lazyfreelazyfreeQ異步回收大對象(元素數 >64 的 list/set/hash/zset),避免主線程長時間釋放

四、BIO 處理流程

在這里插入圖片描述

  1. 任務提交(主線程)

    • 根據任務類型分配 BIO 任務;
    • 加鎖后將任務追加到對應隊列尾部;
    • 通過條件變量喚醒等待的 BIO 線程;
  2. 任務消費(BIO 線程)

    • 阻塞等待新任務到來;
    • 取出并執行對應系統調用或對象釋放;
    • 任務完成后釋放任務結構,繼續等待;

通過引入專門的 BIO 后臺線程隊列,Redis 將所有可能導致短時阻塞的系統調用與大對象回收異步化處理,從而最大限度地保障主線程的低延遲和高吞吐能力。


Redis 多線程架構

Redis 自身單進程單線程模型極大簡化了并發控制、保證了命令執行的原子性,但也限制了吞吐能力。相比 Memcached 能夠通過多線程輕松跑出百萬級 TPS,Redis 單實例 TPS 往往在 10–12 萬左右,線上峰值也多在 2–4 萬,難以充分利用現代 16+ 核服務器。為解決這一痛點,Redis 6.0 在不改動現有核心執行邏輯的前提下,引入了可選的 IO 多線程模型,以并行化網絡讀寫與協議解析,從而在保留主線程執行安全的同時,實現 1–2 倍的性能提升。


一、主線程職責

  • 事件驅動 Loop(ae:監聽客戶端連接、讀寫事件與定時任務,不變;
  • 命令執行:所有實際的業務命令處理邏輯繼續在單一主線程中運行,保持原子性與簡單的調度;
  • 核心任務分發:在網絡 IO 上,主線程將讀寫請求委派給 IO 線程;在命令執行完畢后,將回復操作同樣回交給 IO 線程處理。

二、IO 線程設計

  • 目標:并行化耗時主要集中在三處:網絡讀取、協議解析與響應寫入;
  • 配置:可通過 io-threadsio-threads-do-reads 參數開啟與設置線程數(典型 4–8 個);
  • 模型:主線程負責將待讀或待寫的 client 對象加入對應隊列,IO 線程異步批量拉取并行處理,處理完后主線程再繼續后續邏輯。

三、命令處理完整流程

Redis 6.0 的多線程處理流程如圖所示。主線程負責監聽端口,注冊連接讀事件。當有新連接進入時,主線程 accept 新連接,創建 client,并為新連接注冊請求讀事件。

在這里插入圖片描述

  1. 連接與讀事件注冊

    • 主線程 accept() 新連接,創建 client,注冊可讀事件;
  2. 并行讀取與解析

    • 讀事件觸發時,主線程不直接讀取,而將 client 加入待讀取隊列;
    • 當一輪事件循環結束,發現待讀取鏈表非空,主線程將所有待讀 client 分派給 IO 線程;
    • IO 線程并行讀取各自 client->fd,將原始數據填入 client->querybuf,并執行協議解析,填充 client->argc/argv
    • IO 線程處理完所有任務后更新待處理計數,主線程自旋等待計數歸零。
  3. 命令執行

    • 主線程依次取出已解析的命令,調用原有 redisCommand 處理函數執行;
  4. 并行響應寫入

    • 執行結束后,主線程通過 addReply* 系列將結果填入 client->buf,并將 client 加入待寫隊列;
    • 將隊列再次分派給 IO 線程并自旋等待;
    • IO 線程并行將 client->buf 寫回各自連接,完成響應;
    • 主線程檢測所有寫入完成后,繼續下一輪事件循環。

四、多線程方案優劣

優點缺點與瓶頸
- 并行化網絡 IO 和協議解析,減少主線程阻塞,整體 TPS 提升 1–2 倍- 命令執行與事件調度仍集中于單一主線程,難以突破核心邏輯瓶頸
- 利用多核 CPU 優化網絡吞吐,客戶端連接并發性能更好- IO 批量處理模式需要“先讀完再寫回”,客戶端間相互等待,增加延遲抖動
- 保留原子性與簡潔性,無需改動現有命令實現- 主線程自旋等待 IO 線程完成,若任務少也會高頻自旋,浪費 CPU 資源
- 部分場景下性能提升有限,無法替代真正的多線程命令處理模型

整體來看,Redis 6.0 的 IO 多線程是一次低侵入式的性能優化,能在不破壞兼容性與原子性的前提下帶來可觀提升。但要實現數量級躍升,還需將命令處理、事件調度等核心邏輯多線程化,解耦互斥等待,并逐步演進為真正的全棧并行模型。


復制架構原理

為了避免單點故障、提高可用性與讀性能,必須對數據進行多副本存儲。Redis 作為高性能的內存數據庫,從一開始就內建了主從復制功能,并在各個版本迭代中不斷優化復制策略。

在這里插入圖片描述


一、復制架構原理

在這里插入圖片描述

  • 多層嵌套復制:一個 Master 可以掛載多個 Slave,Slave 也可繼續掛載更多下游 Slave,形成樹狀層級結構。
  • 寫操作分發:所有寫命令僅在 Master 節點執行,執行完后即時分發給下游所有 Slave,保證數據一致。
  • 讀寫分離:Master 只負責寫請求,所有讀請求由 Slave 處理。這種架構既消除了單點故障風險,又通過 N 倍 Slave 并發提升了讀 TPS。

此外,Master 在向 Slave 分發寫命令的同時,會將寫指令保存到復制積壓緩沖區(replication backlog),以便短時斷連的 Slave 重連后增量同步。


二、同步方式對比

同步類型描述優勢劣勢
全量同步Master 生成 RDB 快照并傳輸給 Slave,同時發送緩沖區積壓命令,Slave 全量重建數據。數據完整,適用于首次同步構建 RDB 和網絡傳輸壓力大,耗時長
增量同步Master 僅發送自上次同步位置之后的寫命令,無需生成 RDB。輕量、帶寬占用極低、無 RDB 構建延遲依賴緩沖區容量和斷連時長,容易導致全量重試

三、psync 與 psync2 優化

  1. psync(Redis 2.8+)

    • 引入復制積壓緩沖區
    • Slave 重連時上報 runid 與偏移量
    • 若 runid 一致且偏移仍在緩沖區,則返回 CONTINUE,進行增量同步;否則觸發全量同步
  2. psync2(Redis 4.0+)

    • runid 升級為 replidreplid2
    • RDB 文件中存儲 replid 作為 aux 信息,重啟后可保留 replid
    • 切主時,通過 replid2 支持跨主機增量同步

相比早期版本,psync2 在短鏈路抖動、Slave 重啟和主庫切換等場景中,均能在更多情況下保持增量同步,顯著降低性能開銷與恢復時間。


四、復制連接與授權流程

在這里插入圖片描述

  1. 連接檢測

    • Slave 向 Master 發送 PING → 收到 PONG 則可用
  2. 鑒權(若啟用密碼)

    • Slave 發送 AUTH <masterauth>
  3. 能力協商

    • Slave 通過 REPLCONF 上報自身 IP、端口及支持的 eofpsync2 能力
  4. 同步請求

    • Slave 發送 PSYNC <replid> <offset>
    • Master 根據 replid、replid2 與復制積壓緩沖,決定全量或增量

五、復制過程詳析

5.1 增量同步流程

  • Master 返回 CONTINUE <replid>
  • Slave 將自身 replid 更新為 Master 返回的 replid,將原 replid 存為 replid2
  • Master 從偏移量繼續推送寫命令

5.2 全量同步流程

  • Master 返回 FULLRESYNC <replid> <offset>
  • Master 執行 BGSAVE 生成新的 RDB 快照
  • 將 RDB 與復制緩沖區命令一起推送給 Slave
  • Slave 關閉下游子 Slave 連接,清空本地緩沖
  • Slave 寫入臨時 RDB 文件(每 8MB fsync)→ 重命名 → 清庫 → 加載 RDB
  • 重建與 Master 的命令推送通道,并開啟 AOF 持久化

六、注意事項

  • 緩沖區大小:過大會占用過多內存;過小易導致緩沖刷出,觸發全量復制
  • 網絡穩定性:長連接抖動或丟包會影響復制效率
  • 監控復制延遲:及時預警和擴容,避免生產環境中讀數據不一致或延遲過高
  • 主庫切換策略:在切換 Master 前保證所有 Slave 與當前 Master 完成同步

Redis 集群的分布式方案

Redis 的分布式方案主要分為三類:

  1. Client 端分區
  2. Proxy 分區
  3. 原生 Redis Cluster

下面將逐一介紹它們的設計思路、實現方式及各自優缺點


1. Client 端分區

在這里插入圖片描述

1.1 原理與哈希算法

客戶端通過哈希算法決定某個 key 應存儲在哪個分片(Shard)上,常見算法包括:

  • 取模哈希hash(key) % N
  • 一致性哈希:在哈希環上分配虛擬節點,實現動態擴縮容的平滑性
  • 區間分布哈希:實際是取模的變種,將 Hash 輸出映射到固定區間,再由區間決定分片

對于單 key 請求,客戶端直接計算哈希并路由;對于包含多個 key 的請求,客戶端先對 key 按分片分組,再拆分成多條請求并發執行。

在這里插入圖片描述

1.2 DNS 動態管理

由于每個 Redis 分片的 Master/Slave 都有獨立 IP:Port,當發生故障切換或新增 Slave 時,客戶端需更新連接列表。

  • DNS 管理:為每個分片的主/從分別配置不同域名,客戶端定時異步解析域名、更新連接池
  • 負載均衡:按權重將請求在各 Slave 之間輪詢,既可分散讀壓,又無需業務側改動

1.3 優缺點

  • 優點:無中心依賴、邏輯簡單、性能最優(無額外代理),客戶端可靈活控制
  • 缺點:擴展不夠平滑(新增分片需修改客戶端邏輯并重啟)、業務端分片邏輯耦合

2. Proxy 分區方案

在這里插入圖片描述

2.1 架構概覽

客戶端只需連接到統一的 Proxy 層,由 Proxy 完成路由、拆分及聚合:

  • 接收請求 → 解析命令 → 哈希計算 → 路由到對應 Redis → 聚合響應 → 返回客戶端

這樣,客戶端免維護分片信息,真正的分布式邏輯都隱藏在 Proxy 之下。

2.2 典型實現

方案特點擴縮容性能損耗
Twemproxy單進程單線程;實現簡單、穩定;不支持平滑擴縮重啟 Proxy~5–15%
Codis支持在線數據遷移;豐富的 Dashboard;多實例Dashboard+ZK/etcd略高于 Twemproxy
  • Twemproxy:適合小規模、幾乎不擴縮容的場景;但單線程模型對多 key 請求性能有限
  • Codis:基于 Redis 擴展的 Slot 方案,提供 dashboard 管理,支持在線擴縮容

在這里插入圖片描述

2.3 優缺點

  • 優點:客戶端無需感知分片;擴縮容僅改 Proxy,運維便利
  • 缺點:增加訪問中間層,帶來約 5–15% 的性能開銷;系統更復雜

3. 原生 Redis Cluster

在這里插入圖片描述

3.1 Slot 與 Gossip 架構

  • 16384 個 Slot:啟動時通過 CLUSTER ADDSLOTS 將 Slot 分配到各節點,key 經 CRC16 哈希后落在具體 Slot
  • Gossip 協議:節點間去中心化通信,更新拓撲無需中心節點,操作通過 cluster meet 等命令擴散

3.2 讀寫與重定向

在這里插入圖片描述

  • Smart Client 緩存 Slot→節點映射
  • 若請求到錯節點,返回 MOVEDASK,包含正確節點信息,客戶端解析后重定向
  • 遷移過程中,新舊節點返回 ASK 并引導客戶端臨時訪問遷移節點

3.3 在線擴縮容與數據遷移

在這里插入圖片描述

  1. cluster meet 加入新節點(無 Slot,不可讀寫)
  2. 源節點 cluster setslot slot migrating,目標節點 … importing
  3. cluster getkeysinslot + migrate 遷移 key 數據;遷移期間阻塞該進程
  4. cluster setslot slot nodeid 分配 Slot
  5. 為新主節點添加 Slave:使用 cluster replicate,Slave 只能掛到 Master

縮容則相反:先遷移 Slot,再 cluster forget 下線節點(并加入禁止列表)。

3.4 優缺點

  • 優點:社區官方實現;在線擴縮容;無中心依賴;自動故障轉移
  • 缺點:Slot 與 key 映射占內存;遷移阻塞導致卡頓;復制鏈路單層限制了讀擴展

4. 對比與選型建議

維度Client 分區Proxy 分區Redis Cluster
客戶端維護需 Smart Client
擴縮容平滑度中(Proxy 重啟)高(在線遷移)
性能開銷最小中等(5–15%)較低
運維復雜度業務側Proxy 層集群管理
成熟度通用方案Codis、Twemproxy官方支持
  • 讀密集場景:若對擴縮容需求不強,且對性能最敏感,可考慮 Client 分區。
  • 寫擴展與在線遷移:需平滑擴縮容,且可接受少量代理層開銷,推薦 Codis 或 Redis Cluster。
  • 大規模復雜部署:傾向官方 Redis Cluster,享受社區生態和原生工具支持。

三種方案各有側重:Client 分區最輕量、性能最高;Proxy 分區運維便利;Redis Cluster 原生、彈性最佳。實際生產中,可根據業務特性和運維成本做權衡。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84737.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84737.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84737.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

React useEffect和useEffectLa

原理把對象以樹的形式存檔&#xff0c;根據URL進行匹配渲染對應組件 useEffect 和useLayoutEffect區別 useEffect中的回調函數放在異步任務隊列中&#xff0c;是異步的&#xff0c;會在React渲染&#xff0c; dom 元素更新&#xff0c;瀏覽器繪制完成之后才會執行 useLayout…

multiprocessing多進程使用案例

multiprocessing — 基于進程的并行&#xff1a;https://docs.python.org/zh-cn/3.11/library/multiprocessing.html import sys from fastchat.serve.controller import Controller from fastchat.serve.model_worker import ModelWorker from fastchat.serve.openai_api_ser…

鴻蒙OSUniApp 開發實時天氣查詢應用 —— 鴻蒙生態下的跨端實踐#三方框架 #Uniapp

使用 UniApp 開發實時天氣查詢應用 —— 鴻蒙生態下的跨端實踐 在移動互聯網時代&#xff0c;天氣應用幾乎是每個人手機中的"標配"。無論是出行、旅游還是日常生活&#xff0c;實時獲取天氣信息都極為重要。本文將以"實時天氣查詢應用"為例&#xff0c;詳…

藍橋杯178 全球變暖

題目描述 你有一張某海域 NxN 像素的照片&#xff0c;"."表示海洋、"#"表示陸地&#xff0c;如下所示&#xff1a; ....... .##.... .##.... ....##. ..####. ...###. ....... 其中"上下左右"四個方向上連在一起的一片陸地組成一座島嶼。例如上…

第五十二節:增強現實基礎-簡單 AR 應用實現

引言 增強現實(Augmented Reality, AR)是一種將虛擬信息疊加到真實世界的技術,廣泛應用于游戲、教育、工業維護等領域。與傳統虛擬現實(VR)不同,AR強調虛實結合,用戶無需完全沉浸到虛擬環境中。本文將通過Python和OpenCV庫,從零開始實現一個基礎的AR應用:在檢測到特定…

青少年編程與數學 02-019 Rust 編程基礎 23課題、web服務器

青少年編程與數學 02-019 Rust 編程基礎 23課題、web服務器 一、單線程Web 服務器基本實現步驟完整代碼示例運行結果項目結構注意事項擴展方向 二、多線程Web服務器1. 基本架構設計2. 完整實現代碼項目文件結構文件內容Cargo.tomlsrc/main.rssrc/lib.rsstatic/hello.htmlstatic…

(14)JVM彈性內存管理

文章目錄 &#x1f680; JVM彈性內存管理&#xff1a;K8s環境下的內存優化終極攻略? TL;DR&#x1f635; 等等&#xff0c;為什么我需要關心這個&#xff1f;&#x1f6e0;? 五步搞定彈性內存&#xff08;拯救你的Java應用&#xff09;1?? JVM參數調教2?? 監控指標全覆蓋…

Spring Boot集成Spring AI與Milvus實現智能問答系統

在Spring Boot中集成Spring AI與Milvus實現智能問答系統 引言 隨著人工智能技術的快速發展&#xff0c;智能問答系統在企業中的應用越來越廣泛。然而&#xff0c;傳統的問答系統往往面臨AI幻覺&#xff08;Hallucination&#xff09;問題&#xff0c;即生成不準確或無意義的回…

電腦網絡如何改ip地址?ip地址改不了怎么回事

在日常使用電腦上網時&#xff0c;我們有時會遇到需要更改IP地址的情況&#xff0c;比如訪問某些受限制的網站、解決網絡沖突問題&#xff0c;或者出于隱私保護的需求。然而&#xff0c;許多用戶在嘗試修改IP地址時可能會遇到各種問題&#xff0c;例如IP地址無法更改、修改后無…

SQL進階之旅 Day 1:高效表設計與規范

SQL進階之旅 Day 1&#xff1a;高效表設計與規范 開篇 歡迎來到為期30天的“SQL進階之旅”系列的第一天&#xff01;今天我們將從數據庫表設計的基礎入手&#xff0c;討論如何通過合理的表設計來提升數據庫性能。這不僅是每位數據庫開發工程師的基本功&#xff0c;也是解決實…

【MySQL】第11節|MySQL 8.0 主從復制原理分析與實戰

一、MySQL主從復制基礎 1. 核心概念 定義&#xff1a; MySQL主從復制是將主庫&#xff08;Source/Master&#xff09;的數據變更同步到一個或多個從庫&#xff08;Replica/Slave&#xff09;的機制&#xff0c;默認采用異步復制&#xff0c;支持全庫、指定庫或表的同步。 角…

怎么判斷一個Android APP使用了Cocos 這個跨端框架

要判斷一個 Android 應用是否使用了 Cocos 跨端框架&#xff0c;可以通過以下步驟進行驗證&#xff1a; 一、安裝包結構分析 1. 解壓 APK 將 .apk 文件重命名為 .zip 并解壓&#xff0c;檢查以下特征文件&#xff1a; ? lib/ 目錄&#xff1a; Cocos 引擎的核心原生庫文件通常…

刪除word中由奇偶頁和頁碼1設置多出來的空白頁

問題&#xff1a; 在調整畢設論文格式時&#xff0c;要求奇偶頁眉設置不同&#xff0c;且摘要頁的頁碼是1&#xff08;I&#xff09;。如果摘要頁在整個文檔的第偶數頁&#xff0c;將其頁碼設置為1后會變為奇數頁&#xff0c;word為了湊齊奇偶頁&#xff0c;會在摘要前增加一個…

# 探索自然語言處理的奧秘:基于 Qwen 模型的文本分類與對話系統實現

探索自然語言處理的奧秘&#xff1a;基于 Qwen 模型的文本分類與對話系統實現 在當今數字化時代&#xff0c;自然語言處理&#xff08;NLP&#xff09;技術正以前所未有的速度改變著我們的生活和工作方式。從智能語音助手到自動文本生成&#xff0c;從情感分析到機器翻譯&…

Linux Shell 切換

在 Linux 系統中&#xff0c;切換至 Bash Shell 在 Linux 系統中&#xff0c;切換至 Bash Shell 的方法如下&#xff1a; 臨時切換到 Bash 直接在終端輸入以下命令&#xff0c;啟動一個新的 Bash 會話&#xff1a; bash 退出時輸入 exit 或按 CtrlD 返回原 Shell。 永久切換…

在Windows上,將 Ubuntu WSL 安裝并遷移到 D 盤完整教程(含 Appx 安裝與遷移導入)

&#x1f4bb; 將 Ubuntu WSL 安裝并遷移到 D 盤完整教程&#xff08;含 Appx 安裝與遷移導入&#xff09; 本文記錄如何在 Windows 系統中手動啟用 WSL、下載 Ubuntu 安裝包、安裝并遷移 Ubuntu 到 D 盤&#xff0c;避免默認寫入 C 盤&#xff0c;提高系統性能與可維護性。 ?…

doucker 掛載卷

在 Docker 中&#xff0c;掛載卷&#xff08;Volumes&#xff09;是一種非常重要的功能&#xff0c;它允許你將宿主機的文件系統與容器的文件系統進行共享。掛載卷不僅可以用于持久化數據&#xff0c;還可以用于在宿主機和容器之間傳遞文件。 掛載卷的類型 Docker 支持多種類型…

BLIP3-o:一系列完全開源的統一多模態模型——架構、訓練與數據集

摘要 在近期關于多模態模型的研究中&#xff0c;將圖像理解與生成統一起來受到了越來越多的關注。盡管圖像理解的設計選擇已經得到了廣泛研究&#xff0c;但對于具有圖像生成功能的統一框架而言&#xff0c;其最優模型架構和訓練方案仍有待進一步探索。鑒于自回歸和擴散模型在…

數據分析案例-基于紅米和華為手機的用戶評論分析

&#x1f935;?♂? 個人主頁&#xff1a;艾派森的個人主頁 ?&#x1f3fb;作者簡介&#xff1a;Python學習者 &#x1f40b; 希望大家多多支持&#xff0c;我們一起進步&#xff01;&#x1f604; 如果文章對你有幫助的話&#xff0c; 歡迎評論 &#x1f4ac;點贊&#x1f4…

基礎框架 兼容視頻格式

基礎框架 兼容視頻格式 修改 \src\components\Upload\src\BasicUpload.vue 數據庫新增 vue <template><div class"w-full"><div class"upload"><div class"upload-card"><!--圖片列表--><divclass"uploa…