Redis的阻塞
Redis的阻塞問題主要分為內在原因和外在原因兩大類,以下從這兩個維度展開分析:
一、內在原因
1. 不合理使用API或數據結構
-
Redis 慢查詢
-
Redis 慢查詢的界定
- 定義:Redis 慢查詢指命令執行時間超過預設閾值(默認 10ms)的操作,僅統計命令執行階段耗時
- ?
slowlog-log-slower-than
?:閾值(單位微秒),默認 10000(10ms),建議調整為 1000(1ms) - ?
slowlog-max-len
?:慢查詢日志隊列長度,默認 128,建議提升至 1000+ 避免日志覆蓋。
-
典型慢查詢命令及風險場景
-
高時間復雜度命令(O(N)及以上)
-
全量遍歷類:
- ?
KEYS *
?:遍歷所有鍵(復雜度 O(n)),可能引發長時間阻塞。 - ?
SMEMBERS
?:獲取集合全部元素(O(n)),百萬級數據時耗時顯著。
- ?
-
聚合計算類:
- ?
SORT
?:排序操作(O(n log n)),大列表排序時性能驟降。 - ?
ZUNIONSTORE
?/SUNION
?:多集合交并操作(O(n)),數據量越大耗時越長。
- ?
-
大范圍查詢類:
- ?
HGETALL
?:獲取哈希所有字段(O(n)),10MB+ 的 Hash 操作延遲顯著。 - ?
LRANGE 0 -1
?:獲取列表全部元素(O(n)),可能占用主線程數秒。
- ?
-
-
- BigKey 操作
-
定義:存儲大量數據的 Key(如 10MB Hash、百萬元素 List)
-
典型風險命令:
- ?
DEL
?:刪除 BigKey 時觸發內存回收阻塞(單線程模型下耗時極長)。 - ?
GET
?/SET
?:序列化/反序列化大 Value 時耗時增加(如 AOF 重寫階段)。 - ?
EXPIRE
?:對大 Key 設置過期時間可能引發后續淘汰阻塞。
- ?
-
非顯式高耗時操作
- Pipeline 不當使用:一次性發送過多命令導致單次執行時間超過閾值。
- Lua 腳本阻塞:執行復雜腳本(如循環遍歷大數據)時未分批次處理。
- 事務(MULTI/EXEC) :事務中包含多個高耗時命令時整體被視為慢查詢。
-
-
排查與優化建議
-
查看日志:
SLOWLOG GET [n] # 獲取最近 n 條慢查詢記錄(含命令、耗時、客戶端 IP) SLOWLOG LEN # 統計當前日志數量
?
-
日志字段解析:
- ?
id
?:唯一標識 - ?
timestamp
?:執行時間戳 - ?
duration
?:耗時(微秒) - ?
command
?:完整命令及參數。
- ?
-
優化策略
-
拆分 BigKey:
- 將大 Hash 拆分為多個子 Key,通過分片降低單次操作負載。
- 使用
LPOP
?/RPOP
? 分批次刪除大 List,避免DEL
? 阻塞。
-
配置調優:
- 動態調整閾值:
CONFIG SET slowlog-log-slower-than 1000
?。 - 禁用高風險命令:通過
rename-command
? 屏蔽KEYS
?。
- 動態調整閾值:
-
替換高復雜度命令:
- 用
SCAN
? 替代KEYS
?,HSCAN
? 替代HGETALL
?。 - 對排序需求改用
ZRANGE
? 或客戶端計算。
- 用
-
-
監控與預警
-
工具:
- ?
redis-cli --bigkeys
?:掃描內存中的 BigKey。 - Prometheus + Grafana:可視化監控慢查詢頻率及耗時分布。
- ?
-
告警規則:
- 單命令耗時 > 5ms 時觸發告警。
- 慢查詢日志長度連續增長時排查潛在性能瓶頸。
?
-
-
-
總結:慢查詢命令速查表
命令類型 典型命令 風險場景 優化方案 全量遍歷 ? KEYS *
?、SMEMBERS
?鍵數量多、集合元素量大 改用 SCAN
?、分片存儲聚合計算 ? SORT
?、ZUNIONSTORE
?數據量大、多集合操作 客戶端計算、預聚合 大范圍查詢 ? HGETALL
?、LRANGE 0 -1
?Hash/List 體積大 分批次獲取、壓縮存儲格式 BigKey 操作 ? DEL
?、GET
?(大 Value)內存回收、序列化開銷 漸進式刪除、拆分 Key
-
2. CPU飽和
-
CPU飽和的定義 :
- CPU飽和指Redis單核CPU使用率長期接近或達到100%的臨界狀態。由于Redis采用單線程模型,所有請求由主線程順序處理,一旦CPU滿載會導致命令隊列積壓、響應延遲暴增,甚至引發服務雪崩。這種現象在高并發或復雜操作場景下尤為危險
-
判斷并發量是否達極限
-
?
redis-cli --stat
?命令分析 每秒輸出一次統計信息,重點關注requests
?字段(即OPS,每秒操作數)redis-cli --stat # 典型CPU飽和時的輸出特征: # 1. 每秒處理請求量持續在5萬+(普通服務器極限約8-10萬OPS) # 2. 客戶端連接數(clients)持續高位且無明顯波動
?
-
?
top
?命令監控 直接觀察Redis進程的CPU使用率top -p $(pgrep redis-server) # 當CPU使用率≥95%且持續不降時,可判定為飽和狀態
-
?
INFO commandstats
?分析命令耗時 觀察高頻命令的usec_per_call
?(單次調用微秒數cmdstat_hset:calls=198757512,usec=27021957243,usec_per_call=135.95 # 正常O(1)命令應≤10微秒,若值異常高(如135μs)可能存在配置或數據結構問題
-
-
問題根源分析
-
高算法復雜度命令
-
過度內存優化
- 修改
hash-max-ziplist-entries
?等參數過度壓縮數據結構,導致操作復雜度從O(1)退化為O(n)
- 修改
-
持久化操作競爭
- Fork阻塞
- AOF刷盤
-
連接數過載 : 短連接頻繁創建或
maxclients
?設置過低,導致TCP握手/斷連消耗CPU資源
-
3. 持久化阻塞
-
RDB生成:
BGSAVE
?觸發fork操作時,若內存過大(如10GB),復制頁表可能導致主線程暫停(典型耗時約20ms/GB)。 -
AOF重寫:
BGREWRITEAOF
?期間,主線程需將緩沖區數據追加到新AOF文件,可能因磁盤壓力大而阻塞。-
優化方案:
- 調整RDB觸發頻率,避免高峰期執行。
- 使用
appendfsync everysec
?替代always
?,降低磁盤I/O壓力。 - 關閉透明大頁(THP),避免內存頁復制效率降低。
-
二、外在原因
1. CPU競爭
-
Redis部署在多核服務器時,若與其他進程競爭CPU資源,或父子進程(如RDB/AOF重寫)綁定同一核心,會導致性能下降。
-
優化方案:
-
將Redis部署在專用服務器,避免資源爭搶。
-
調整進程綁定策略(如父進程與子進程綁定不同核心)。
-
錯誤的現象 :
flowchart TDsubgraph CPU核心1A[Redis主線程\n(父進程)]B[RDB/AOF子進程]endA -->|fork| BA -->|處理客戶端請求\n(高優先級)| C[CPU時間片]B -->|生成快照/重寫AOF\n(低優先級)| CC -->|資源搶占| D[響應延遲增加]
-
正確的現象
flowchart TDsubgraph 物理CPUsubgraph 核心0-3A[Redis主線程\n(綁定核心0-3)]endsubgraph 核心4-7B[RDB/AOF子進程\n(綁定核心4-7)]endendA -->|fork| BA -->|獨占核心0-3| C[高效處理請求]B -->|獨占核心4-7| D[無干擾持久化]
-
-
-
2. 內存交換(Swap)
-
內存交換是操作系統的內存管理機制,當物理內存不足時,系統會將部分內存中的冷數據(長時間未被訪問)移動到磁盤的 Swap 分區,以騰出內存空間給其他進程使用。
對 Redis 而言,數據原本應完全駐留內存以實現高性能(微秒級響應)。若發生 Swap,訪問被換出的數據需經歷磁盤 I/O(毫秒級),響應延遲驟增 5~10 萬倍。 -
內存交換流程圖
flowchart TDsubgraph 物理內存A[Redis 熱點數據\n(高頻訪問)]B[Redis 冷數據\n(長時間未訪問)]C[其他進程占用的內存]endsubgraph 磁盤Swap分區D[被換出的冷數據]endA -->|持續活躍| E[正常響應(微秒級)]B -->|內存不足觸發交換| F[Swap Out操作]F -->|數據寫入磁盤| DC -->|占用內存增加| FD -->|再次被訪問| G[Swap In操作]G -->|數據加載回內存| H[高延遲響應(毫秒級)]
-
流程關鍵點
-
觸發條件(內存不足)
- Redis 自身內存超限(如未設置
maxmemory
?) - 同一服務器運行其他內存密集型進程(如大數據處理、文件 I/O)
- Redis 自身內存超限(如未設置
-
Swap Out
操作系統將冷數據從內存遷移到 Swap 分區,釋放物理內存空間。
-
Swap In
Redis 需訪問已換出的數據時,觸發磁盤讀取和數據回遷,導致延遲暴增。
-
性能影響
單次 Swap 操作可能引入數毫秒延遲,若高頻觸發則整體吞吐量斷崖式下跌
-
-
-
內存交換的核心原因
-
Redis 內存超限
- 未配置 ?
maxmemory
??:Redis 默認無內存限制,可能無限增長直至觸發 Swap。 - BigKey 或內存碎片:大對象(如 10MB Hash)或碎片化內存占用超出預期
- 未配置 ?
-
操作系統資源競爭
- 多進程共存:同一服務器運行 MySQL、Hadoop 等內存密集型服務,擠占 Redis 可用內存。
- Swap 配置不合理:Linux 默認
vm.swappiness=60
?,內存壓力大時激進換出數據。
-
硬件限制
- 物理內存不足:服務器內存容量無法支撐 Redis 數據集規模
- 磁盤性能差:機械硬盤的 Swap 操作延遲遠高于 SSD(如 10ms vs 0.1ms)。
-
-
監控與診斷工具
-
檢查 Swap 使用量
# 查看 Redis 進程 Swap 情況 redis-cli info | grep process_id # 獲取 PID cat /proc/<PID>/smaps | grep -i swap
-
性能分析工具
- ?
redis-cli --latency
?:檢測 Redis 響應延遲波動。 - ?
vmstat
?:監控系統級 Swap I/O 頻率(si/so
? 列)
- ?
-
3. 網絡問題
-
連接數過多:短連接頻繁創建或
maxclients
?設置過低,導致TCP連接處理消耗CPU資源。 -
帶寬不足:高吞吐場景下網絡打滿,或使用
MONITOR
?命令記錄所有請求。-
優化方案:
- 使用連接池管理長連接,設置
timeout
?自動關閉空閑連接。 - 禁用
MONITOR
?,通過Pipeline
?批量請求減少網絡往返。
- 使用連接池管理長連接,設置
-
三、總結與優化策略
阻塞類型 | 關鍵表現 | 優先級 | 解決方案 |
---|---|---|---|
大Key操作 | 單命令執行時間過長 | 高 | 拆分Key、改用分片或壓縮結構 |
持久化fork延遲 | ?latest_fork_usec ?值高 | 高 | 降低RDB頻率、優化內存頁管理 |
CPU競爭 | 服務器整體CPU飽和 | 中 | 隔離部署、綁定CPU核心 |
內存交換 | ?used_memory_rss ?異常高 | 緊急 | 禁用Swap、增加物理內存 |
綜合建議:
- 監控工具:使用
SLOWLOG
?、INFO commandstats
?分析慢查詢,redis-cli --bigkeys
?掃描大Key。 - 架構設計:采用讀寫分離、集群分片(Redis Cluster)分散負載。
- 配置調優:調整
maxmemory
?、repl-backlog-size
?,優化淘汰策略(如volatile-lru
?)。
通過上述措施,可顯著降低Redis阻塞風險,提升系統穩定性。
?