本文目錄: 背景
Redis 內存消耗劃分 內存 OOM 會導致哪些問題? 排查思路
是否數據量太大? 是否客戶端輸入緩衝區有問題? 是否複製積壓緩衝區有問題? 是否客戶端輸出緩衝區有問題? 實用命令
模擬 Redis 壓力相關命令 常⽤ Redis 內存排查命令 總結
背景
你是否有過這種困擾:我的數據量非常小,但還是報 OOM 錯誤?
# ⼀個簡單set提示內存不⾜
[root@10-186-61-38 redis]# redis-cli -p 9999 set actionsky 1
(error) OOM command not allowed when used memory > 'maxmemory'.
首先我給大家解釋下,Redis 的 OOM 分兩種。
-
⼀種是因 Redis 使用內存超出 OS 物理內存,OS 將 Redis 進程殺死。
-
另⼀種是 Redis 使用內存超過
maxmemory
參數配置,引發 Redis Server 層 OOM。
OOM 是 Redis 最常見的內存故障,它影響很大:
-
故障發生時,進程並不會退出,能讀但無法寫入。
-
配置了 allkeys-lru、allkeys-lfu 等內存淘汰策略場景下,會有大量鍵失效,導致緩存命中率急劇下降。
本文中,我會給大家分享下該種內存問題的排查方向及運維命令。
Redis 內存消耗劃分
簡短介紹下 Redis 內存消耗劃分情況,爲下文診斷提供思路。上圖可以總結 Redis 消耗內存分如下幾塊:
-
對象內存:理論上佔用最大,存儲所有業務數據,如字符串類型、哈希類型對象等。
-
客戶端內存:包括輸入客戶端(查詢或寫入命令)、輸出客戶端使用的內存,因爲不受 maxmemory 參數控制,這塊我們需重點排查。
-
複製積壓緩衝:所有從庫客戶端共享、保存固定大小的寫入命令用於從庫失連後數據補償。
-
Redis 自身內存:存儲數據元數據信息、過期鍵字典等。
-
AOF 緩衝區:AOF 持久化、重寫緩衝區,⼀般佔用很少,基本不需要關注。
內存 OOM 會導致哪些問題?
1. Redis 無法寫入,只能讀取。
2. Redis 大量鍵被逐出內存或過期,導致 Redis 查詢效率降低(maxmemory-policy 配置爲非默認值 noeviction 時)。
排查思路
注意:下文不做特別說明的話,我的 maxmemory 設置爲 1G,其它任何參數爲默認。
是否數據量太大?
使用 redis-benchmark 持續灌入數據,
檢查內存使用情況,發生 OOM 狀態時 used_memory ⼀定會大於 maxmemory。
檢查數據對象內存和其它內存使用情況如下圖:
這裏有必要說明下 overhead.total,它包括除數據外 Redis 消耗的所有內存,比如前面提到的複製緩衝區、客戶端輸入輸出緩衝區等,另外還包括⼀些元數據如 overhead.hashtable,它是數據庫中元數據消耗的內存大小,包括以下三項:
-
整個數據庫是⼀種 hash 表,首先就是這張 hash 表使用的內存。
-
每⼀個 key-value 對都有⼀個 dictEntry 來記錄他們的關係,元信息便包含該 db 中所有 dictEntry 使用的內存。
-
redis 使用 redisObject 來描述 value 所對應的不同數據類型(string、list、hash、set、zset),那麼 redisObject 佔用的空間也計算在元數據。
大家對這個現象可能有點疑惑,爲啥我明明設置 maxmemory 爲 1G,你 Redis 只給我存了 990 多 M 數據就滿了?
很好理解,根據上面測試可知數據達到⼀定規模後,因需消耗額外的元數據、緩存內存,Redis 最終將超過 maxmemory 而 OOM。
是否客戶端輸入緩衝區有問題?
製造輸入緩衝區壓力(防止干擾,先清空數據再壓測)
# 關鍵參數解釋
-d 表示每個set值的大小,單位爲字節
-c 啓多少個連接
壓測幾秒鐘後,觸發 OOM,
檢查輸入緩衝區內存消耗,能看到客戶端輸入緩衝區消耗總量爲 2.4G左右,遠遠超過 maxmemory 參數設置。
那我如何找到消耗內存量最大的那個連接呢?
可通過運行上述檢查命令,定位到各客戶端輸入緩衝區的內存消耗(由大到小排序)。
⼀般如果定位到有連接異常,可以使用如下命令殺掉。
# 例如殺掉上圖中 id=51421 的連接
127.0.0.1:9999> CLIENT KILL ID 51421
(integer) 1
是否複製積壓緩衝區有問題?
爲測試方便,我直接把複製積壓緩衝區配置爲 800M。
開啓 redis-benchmark 壓測進程,
檢查複製積壓緩衝區內存消耗,可以看到因爲緩衝區設置過大,數據量才存儲 190 多 M,Redis 就無法寫入了。
是否客戶端輸出緩衝區有問題?
若客戶端輸出緩衝區太大如何排查?⼀般該場景比較少見,常見於用到了 redis 的 monitor
命令。
注意:monitor 命令功能像 MySQL 的 general-log,能打印 Redis 所有執行的命令。在生產環境極少使用或禁用。
先開啓 monitor 命令,
通過 redis-benchmark 製造輸出緩衝區壓力。
測試⼀段時間後觀察 Redis 內存消耗,
此時數據庫無法寫入,
檢查輸出緩衝區各客戶端連接內存消耗、輸出緩衝區總消耗內存如下,
可以看到輸出緩衝區總內存已遠大於 maxmemory
限制,此時內存自然就 OOM。
實用命令
上文排查過程有些 Redis 運維命令我認爲比較實用,整理如下:
模擬 Redis 壓力相關命令
# 1. 持續給Redis灌數據
redis-benchmark -p 9999 -t set -r 100000000 -l
# 2. 模擬輸入緩衝區過大
redis-benchmark -p 9999 -q -c 10 -d 102400000 -n 10000000 -r 50000 -t set
# 3. 模擬輸出緩衝區過大
redis-benchmark -p 9999 -t get -r 5000000 -n 10000000 -d 100 -c 1000 -P 500 -l
常用 Redis 內存排查命令
# 1. 快速查看Redis內存是否夠用
redis-cli -p 9999 info memory |egrep
'(used_memory_human|maxmemory_human|maxmemory_policy)'
# 2. 檢查複製積壓緩衝區使用情況
redis-cli -p 9999 memory stats|egrep -A 1
'(total.allocated|replication.backlog)'
# 3. 檢查客戶端輸入緩衝區內存使用總量
redis-cli -p 9999 client list| awk 'BEGIN{sum=0}
{sum+=substr($12,6);sum+=substr($13,11)}END{print sum}'
# 4. 檢查客戶端輸入緩衝區各客戶端連接的內存情況
redis-cli -p 9999 client list|awk '{print substr($12,6),$1,$12,$18}'|sort -
nrk1,1 | cut -f1 -d" " --complement
# 5. 檢查客戶端輸出緩衝區內存使用總量
redis-cli -p 9999 client list| awk 'BEGIN{sum=0} {sum+=substr($16,6)}END{print
sum}'
# 6. 檢查客戶端輸出緩衝區各客戶端連接的內存使用排序
redis-cli -p 9999 client list|awk '{print substr($16,6),$1,$16,$18}'|sort -
nrk1,1 | cut -f1 -d" " --complement |head -n10
# 7. 檢查數據對象使用內存總量
redis-cli -p 9999 memory stats|grep -A 1 'dataset.bytes'
總結
-
Redis 內存問題大部分可以通過上述排查思路進行定位。
-
跟大家分享了⼀些常用 Redis 內存排查命令,希望對大家有幫助。
-
如果大家覺命令執行起來不夠方便,我整理了⼀份 Redis 內存檢查腳本(篇幅稍長)有興趣可以移步查閱:
https://www.jianshu.com/p/a508187bc093
腳本執行效果:
文章推薦:
社區近期動態
本文分享自微信公衆號 - 愛可生開源社區(ActiontechOSS)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。