文章中相關知識點在往期已經更新過了,如果有友友不理解可翻看往期內容
出現腦裂問題怎么保證集群還是高可用的
什么是腦裂問題
腦裂說的就是當我們的主節點沒有掛,但是因為網絡延遲較大,然后和主節點相連的哨兵通信較差,之后主從之間的同步就會受影響,主節點和哨兵之間的信號也不好,但是客戶端和主節點之間還能進行通信,此時向主節點更新了一條數據,而由于網絡問題,從節點未能同步,且由于哨兵集群和主節點之間的通信較差每次ping都收到主節點的恢復,那么哨兵集群就會判斷主節點從主觀下線到了客觀下線(超過了設置的客觀下線的數量),那么此時哨兵就會認為主節點掛了,此時會選取一個哨兵作為主節點,然后強行修改原來主節點的配置將其設置為該slave節點的從節點(然后向新的主節點發起主從同步的時候就會進行全量同步,因此原來主節點中更新的那個消息就會徹底丟失),但是此時的問題就是新的從節點并沒有原來更新的那條數據的信息,這樣就造成了數據的不一致。這就是腦裂問題
解決的核心思路就是
由于是因為主節點能處理客服端的更新操作和,網絡延遲導致主從不一致,然后又由哨兵選取從節點變更為主節點導致的數據丟失,那么我們就可以采取這樣的操作,即當發生這種網絡延遲問題時,我們就拒絕客服端發過來的請求,這樣就保證網絡延遲期間主從的一致性,此時就算哨兵將從節點設置為新的主節點也沒關系,因為數據被沒有被更新,從節點中還是有所有數據
具體解決
具體解決就可以在配置文件中進行配置主節點發現從節點下線或者通信超時的總數量小于閾值
當主節點發現從節點下線或者通信超時的總數量小于閾值時,那么禁止主節點進行寫數據,直接把錯誤返回給客戶端。
在 Redis 的配置文件中有兩個參數我們可以設置:
- min-slaves-to-write x,與主節點能連接的同的從節點個數只有超過x時才能進行寫操作,否之拒絕寫操作
- min-slaves-max-lag x,主從數據同步的延遲不能超過 x 秒,如果超過,主節點會禁止寫數據。
我們可以把 min-slaves-to-write 和 min-slaves-max-lag 這兩個配置項搭配起來使用,分別給它們設置一定的閾值,假設為 N 和 T。
這就表示這兩個配置項組合后的要求是,主庫連接的從庫中至少有 N 個從庫,和主庫進行數據復制時的 ACK 消息延遲不能超過 T 秒,否則,主庫就不會再接收客戶端的寫請求了。
大key、熱key問題
大key
是什么?
Redis 中的 大 Key(Big Key) 是指存儲的單個 Key 對應的 Value 過大(如字符串類型 Value 體積過大,或集合/列表/哈希等類型的元素數量過多)。這類 Key 會顯著影響 Redis 的性能和穩定性,是實際應用中需要重點規避的問題。
大 Key 的典型場景
- 字符串類型
-
- 存儲大體積數據(如 1MB 以上的 JSON/二進制數據)。
- 示例:緩存一個完整的商品詳情頁 HTML(可能達到幾 MB)。
- 集合/列表/哈希類型
-
- 元素數量過多(如哈希表存儲百萬級字段)。
- 示例:用戶粉絲列表存儲了 100 萬用戶 ID。
- 流(Stream)類型
-
- 消息隊列堆積未及時消費,導致單個流包含大量消息。
大key的危害
- 影響redis的持久化
- 客戶端超時阻塞:由于redis執行命令是單線程的,然后再操作大key時會比較耗時,那么就會阻塞redis,從客戶端這邊看,就是很久很久沒響應
- 引發網絡阻塞。每次獲取大key產生的流量都較大,比如假設一個key的大小是1MB,此時每秒訪問量為1000MB,那么每秒就會產生1000MB的流量,這對于普通的千兆服務器來說是災難性的
- 阻塞工作線程。如果使用了del命令刪除了大key,就會阻塞工作線程可以采用unclink來異步刪除
- 存儲傾斜(內存分布不均):集群再slot分片均勻的情況下,會產生數據和查詢傾斜的情況,存儲大key的Redis節點占用內存多
這里講一下為什么會影響redis的持久化
主要還是redis的fork以及寫時復制這個原因
首先關于aof的刷盤策略:
- always:每次更新數據都進行刷盤,既每次都執行fsync()
- everysecond:每秒刷盤:會創建一個異步任務來執行fsync()
- no:數據到了aof buffer之后就不管了,由操作系統來進行控制刷盤,既永不執行fsync()函數
其實核心都是控制fsync()函數將內核緩沖區中的數據寫到磁盤中
大key對于aof的持久化影響
如果設置的aof刷盤策略是Always策略,主線程再執行命令之后,會將數據寫到AOF日志文件中,然后調用fsync()函數將內核緩沖區中的數據直接寫到磁盤,等磁盤寫操作完該函數才會返回,可以看到redis在這個過程中一直是阻塞等待的,即是單線程處理
所以在使用Always策略的時候,如果寫入的是以一個大key,主線程執行fsync()函數的時候,阻塞時間會比較久,因為當寫入的數據量很大的時候,數據會同步到磁盤這個過程很耗時
如果使用的是everysec或no的刷盤策略的話就持久化過程就不會影響主線程
大key對AOF重寫和RDB的影響
1.對fork過程的影響
主要核心問題,AOF重寫和RDB生成過程其實核心都是需要fork一個子進程,內核會將父進程中的頁表也會復制一份給子進程,雖然子進程是獨立運行的不影響主進程,但是在fork子進程的這個過程中是需要主進程參與的,
而大key說明所占空間比較大,也會導致頁表所占空間增大,所以要是頁表很大那么復制過程也是很耗時的,那么在fork時就會發生阻塞現象
2.因為在fork復制之后,主進程還會處理客戶端的命令,如果客戶端執行執行修改操作,那么主進程會進行寫時復制操作(這個操作是主進程進行的),即將修改的數據拷貝一份到物理內存中,但如果修改的這個key就是大key那么這個復制過程也可能造成阻塞
-
- 示例:對字符串類型的大 Value 按固定大小分塊存儲(如
big_data:part1
,big_data:part2
)。
- 示例:對字符串類型的大 Value 按固定大小分塊存儲(如
- 對于海量數據存儲時,我們也可以通過上面兩種方式先進行拆分key,拆分key之后redis會計算key的哈希值去找到對應的哈希巢slot,然后將這個大key就將其拆成不同的小key,對應不同的哈希槽slot,然后就可以分布到不同的節點,這樣就可以有效解決數據傾斜問題
熱key問題
是什么
Redis 中的 熱 Key(Hot Key) 是指被高頻訪問的單個 Key,其請求量遠高于其他 Key。這類 Key 會導致 Redis 實例或某個分片(Cluster 模式)的負載過高,引發性能瓶頸甚至服務崩潰。熱 Key 問題需要結合業務場景和架構設計來預防和解決。
熱 Key 的危害
- 單節點過載(多級緩存+對key拆分解決)
-
- 在 Redis 集群模式下,熱 Key 可能集中在某個分片(哈希槽),導致該節點 CPU/網絡過載,而其他節點空閑。
- 示例:某商品庫存 Key 每秒接收 10 萬次讀取,遠超單節點處理能力。
- 緩存雪崩風險(一致性要求高就直接加分布式鎖,否之可以使用key的邏輯過期)
-
- 若熱 Key 突然失效(如過期或主動刪除),大量請求穿透到數據庫,可能引發級聯故障。
- 性能抖動(參考大key問題)
-
- 熱 Key 的頻繁訪問可能導致 Redis 主線程阻塞(如大 Value 讀取),影響其他請求的延遲
解決方案
主要:本地緩存+熱key的冗余話存儲+讀寫分離,如果讀多那么可以考慮增加從節點的個數
2. 壓縮數據
- 對字符串類型的 Value 使用壓縮算法(如 gzip、Snappy),讀取時解壓。
- 需權衡 CPU 與內存的消耗(適合讀少寫多的場景)。
3. 設置過期時間
- 對臨時性大 Key 設置 TTL(如
EXPIRE
),避免長期占用內存。
4. 異步刪除
- Redis 4.0+ 支持
UNLINK
命令替代DEL
,后臺異步刪除大 Key。 - 配置
lazyfree-lazy-user-del yes
,自動異步刪除。
5. 限流與熔斷
- 客戶端對大 Key 的訪問做限流(如令牌桶算法),避免突發流量壓垮服務。
1. 本地緩存(多級緩存)
- 在應用層(客戶端)對熱 Key 添加本地緩存(如 Guava、Caffeine),減少對 Redis 的直接訪問。
- 適用場景:讀多寫少,允許短暫數據不一致(設置較短的本地 TTL)。
- 示例:商品庫存讀取時,客戶端緩存庫存值 100ms,期間所有請求直接讀本地緩存。
2. Key 分片(分散壓力)
- 將熱 Key 拆分為多個子 Key,通過哈希或隨機后綴分散到不同節點。
-
- 示例:原 Key
stock:item_123
拆分為stock:item_123:1
、stock:item_123:2
,客戶端隨機訪問一個子 Key。
- 示例:原 Key
- 注意:需確保數據一致性(如所有子 Key 同時更新)。
3. 讀寫分離
- 對熱 Key 啟用讀寫分離,將讀請求分發到從節點(需容忍主從同步延遲)。
- 限制:Redis 集群模式下從節點不分擔讀請求(需 Proxy 或客戶端分片)。
4. 緩存永不過期 + 異步更新
- 對熱 Key 不設置過期時間,通過后臺任務定期更新緩存,避免緩存擊穿。
- 示例:商品庫存每 10 秒異步更新一次,Key 永不過期。
5. 限流與熔斷
- 對熱 Key 的訪問進行限流(如令牌桶算法),超出閾值時熔斷,返回默認值或降級數據。
- 工具:使用 Sentinel、Hystrix 或 Resilience4j 實現。
6. 使用更高效的數據結構
- 優化存儲格式,減少單次訪問的數據量。
-
- 示例:用哈希表存儲商品信息,按需獲取字段(
HGET
替代GET
),避免傳輸冗余數據。
- 示例:用哈希表存儲商品信息,按需獲取字段(
7. Redis 集群擴容
- 對熱 Key 所在的分片進行垂直擴容(提升單節點配置)或水平擴容(增加分片數量,需遷移數據)。
這里講一下key分片策略
方案核心思想
- 冗余存儲
將熱 Key 復制多份,存儲在不同的 Master 節點,例如key:1
(節點1)、key:2
(節點2)。 - 請求分發
客戶端通過輪詢或哈希算法,將請求均勻分發到不同副本,降低單個節點的壓力。
適用場景
- 讀多寫少:寫入頻率低,且容忍一定同步延遲(如緩存熱點新聞)。
- 高并發讀:單 Key 的 QPS 遠超單節點處理能力(如 10萬+/秒)。
- 集群模式:Redis 部署為 Cluster 模式,且 Master 節點數量充足。
優勢
- 分散讀壓力
讀請求被均勻分發到多個節點,避免單節點過載。 - 高可用性
冗余副本分散在不同節點,單個節點故障時,其他副本仍可提供服務(需配合故障轉移)。 - 擴展性
可通過增加副本數量(如key:3
、key:4
)應對更高并發。
潛在問題
1. 數據一致性
- 寫入成本高:每次更新需同步修改所有副本,否則會讀到舊數據。
-
- 示例:更新
key
時需同時更新key:1
、key:2
,若某次更新失敗,會導致數據不一致。
- 示例:更新
- 最終一致性:若副本間同步有延遲,客戶端可能讀到不同版本的數據。
2. 額外存儲開銷
- 冗余副本占用更多內存,若 Key 的 Value 較大(如 1MB),存儲成本顯著增加。
3. 客戶端復雜度
- 客戶端需維護所有副本的位置(如
key:1
、key:2
),并實現負載均衡邏輯。 - 若集群拓撲變化(如節點擴容/縮容),客戶端需動態感知副本分布。
4. 寫入放大效應
- 對熱 Key 的寫入操作會放大 N 倍(N=副本數),可能引發性能問題。
優化方案
1. 異步復制(最終一致性)
- 寫入時僅更新主副本(如
key:1
),通過后臺任務異步同步到其他副本(如key:2
)。 - 代價:讀可能短暫不一致,適合對一致性要求不高的場景(如計數器緩存)。
2. 版本號控制
- 為每個 Key 添加版本號(如
key:1:v100
、key:2:v100
),客戶端讀取時校驗版本號,若不一致則觸發同步。 - 示例:
-
- 寫入時更新所有副本,并遞增版本號。
- 讀取時優先訪問一個副本,若發現版本號落后,觸發同步。
3. 代理層分發
- 使用中間件(如 Redis Proxy 或 Envoy)統一管理副本,客戶端無感知。
-
- 代理層維護副本列表,自動負載均衡讀請求。
- 寫入時由代理層同步更新所有副本。
4. 結合讀寫分離
- 主副本(如
key:1
)處理寫請求,其他副本(如key:2
、key:3
)作為只讀副本。 - 讀請求分發到只讀副本,寫入僅需更新主副本,降低一致性復雜度。
與其他方案的對比
方案 | 優點 | 缺點 | 適用場景 |
冗余分片 | 分散讀壓力,高可用 | 數據一致性難,寫入成本高 | 讀多寫少,容忍最終一致性 |
本地緩存 | 零網絡開銷,響應快 | 數據不一致,內存占用高 | 極高頻讀,允許短暫不一致 |
Key 分片(Hash) | 天然分散壓力,無需維護副本 | 拆分邏輯復雜,需修改業務代碼 | 數據可拆分(如按用戶 ID 分片) |
讀寫分離 | 利用從節點資源,簡單易行 | 主從延遲,集群模式不支持 | 讀占比高,容忍延遲 |
目前已更新系列:
當前:Redis----大key、熱key解決方案、腦裂問題
分布式---raft算法
分布式---CAP&&BASE理論
MySQL----BufferPool、redolog binlog兩階段提交
實習期間git的分枝管理以及最常用的命令-CSDN博客
Redis高級-----持久化AOF、RDB原理
Redis高級---面試總結5種數據結構的底層實現
Redis高級----主從、哨兵、分片、腦裂原理-CSDN博客
Redis高級---面試總結內存過期策略及其淘汰策略
計算機網絡--面試知識總結一
計算機網絡-----面試知識總結二
計算機網絡--面試總結三(Http與Https)
計算機網絡--面試總結四(HTTP、RPC、WebSocket、SSE)-CSDN博客
計算機網絡-------重傳、TCP流量控制、擁塞控制_tcp擁塞控制,擁塞避免-CSDN博客
知識積累之ThreadLocal---InheritableThreadLocal總結
分布式ID多種生成方式-CSDN博客
并發編程之----線程池ThreadPoolExecutor,Excutors的使用及其工作原理