當云數據庫 Tair(兼容 Redis)內存不足時,可能導致Key頻繁被逐出、響應時間上升、QPS(每秒訪問次數)不穩定等問題,進而影響業務運行。如果發現實例內存占滿或收到內存告警,可參考本文判斷內存占用是否長期過高、內存占用是否突然上升、是否發生內存傾斜,并通過拆分大Key,設置過期策略,升級規格等方法解決問題。
內存使用率高的現象分類
內存使用率高,通常分為以下三種情況:
內存使用率在較長一段時間內一直處于較高水位。通常,當內存使用率超過95%時需要及時關注。
內存使用率一直較低,但從某個時間點開始突然上升至較高水位,甚至達到100%。
實例整體的內存使用率較低,但某個數據分片節點的內存使用率接近100%。
請根據不同情況,分別采取措施降低內存使用率。
內存使用率長期處于高水位的解決辦法
查詢現有的Key是否符合業務預期,及時清理無用的Key。
通過緩存分析功能,分析大Key分布和Key的TTL過期策略。具體操作,請參見離線全量Key分析。
分析Key是否有合理的TTL策略。
說明以下示例中,所有的Key均未設置過期時間,建議根據業務需求來衡量,并在應用端設置合理的過期時間。
對大Key進行評估,然后在業務側對大Key進行拆分。
根據業務需求,設置合理的數據逐出策略(即調整maxmemory-policy參數的值)。具體操作,請參見設置實例參數。
說明實例默認的數據逐出策略為volatile-lru,更多信息,請參見數據逐出策略介紹。
根據業務需求,設置合理的過期Key主動刪除的執行頻率(即調整hz參數的值)。具體操作,請參見調整定期任務的執行頻率。
說明hz的取值建議在100以內,如果該值過大將對CPU的使用率產生較大影響。您也可以設置為自動調整(要求實例的大版本為兼容Redis 5.0及以上版本),具體操作,請參見自動調整定期任務的執行頻率。
經過上述步驟優化后,內存使用率依舊較高,可評估升級至更大內存的規格,以承載更多數據并改善性能。具體操作,請參見變更實例配置。
說明在正式升級實例的規格前,您可以先購買一個按量付費的實例,測試要升級到的目標規格是否能夠滿足業務的負載需求,測試完成后可將其釋放。關于如何釋放實例,請參見釋放按量付費實例。
內存使用率突然上升的解決辦法
問題原因
內存使用率突然升高的主要原因如下:
短時間內大量寫入新數據。
短時間內大量創建新連接。
突發訪問產生大量流量超過網絡帶寬,導致輸入緩沖區和輸出緩沖區積壓。
客戶端處理速度跟不上實例的處理速度,導致輸出緩沖區積壓。
解決方案
請依次排查內存使用率突然升高的原因,并參考對應的解決方案解決問題。
排查是否寫入大量新數據
排查方法:
查看性能監控的入流量與寫QPS。如果入流量與寫QPS的趨勢與內存使用率的趨勢一致,說明大量的數據寫入導致內存使用率突然升高。
解決方案:
通過設置Key的過期時間自動清理不再需要的Key,或手動刪除不再需要的Key。
升級實例規格,通過增加內存容量緩解內存使用率升高的問題。詳情請參見變更實例配置。
如果您的實例為標準版,擴容內存規格后仍無法解決內存使用率高的問題,可以考慮升級為集群版,將數據分布到多個數據分片節點上,減輕單個數據分片節點的內存壓力。詳情請參見變更實例配置。
排查是否創建大量新連接
排查方法:
查看性能監控的連接數。如果連接數突然增長,且與內存使用率的趨勢一致,說明大量新建連接導致內存使用率突然升高。
解決方案:
排查是否存在連接泄漏。
設置連接超時時間,自動關閉空閑連接。詳情請參見設置客戶端連接的空閑時間。
排查是否突發流量導致輸入和輸出緩沖區積壓
排查方法:
查看性能監控的出入口流量使用率是否達到100%。
執行
MEMORY STATS
命令,查看clients.normal占用的內存是否過多。說明clients.normal反映了所有普通客戶端連接的輸入和輸出緩沖區所占用的內存總量。
解決方案:
排查是否因客戶端性能問題導致輸出緩沖區積壓
排查方法:
在實例中,執行MEMORY DOCTOR
命令,查看big_client_buf
的值。當big_client_buf=1時,代表至少有一個客戶端的輸出緩沖區占用內存較大。
解決方案:
執行CLIENT LIST
命令,查看哪個客戶端的輸出緩沖區內存占用量(omem
)較大。排查該客戶端應用是否存在性能問題。
數據分片節點內存使用率高的解決方法
現象
如果實例為集群架構,您可能從以下一種或幾種現象發現數據分片節點的內存使用率高:
收到了云監控的內存使用率告警。告警信息中顯示某個數據節點的內存使用率高,超過了閾值。
實例診斷報告顯示內存使用率發生傾斜。
在性能監控頁面查看實例的內存使用率和數據節點的內存使用率,發現實例的內存使用率不高,但某個數據分片節點的內存使用率較高。
問題原因
如果實例的內存使用率不高,但某個數據分片節點的內存使用率較高,說明發生了內存傾斜。
解決方案
檢查是否存在大Key,并拆分大Key
查找大Key
通過離線全量Key分析找出大Key。
更多找出大Key的方法,請參見發現并處理大Key和熱Key。
拆分大Key
例如將含有數萬成員的一個HASH Key拆分為多個HASH Key,并確保每個Key的成員數量在合理范圍。在集群架構實例中,拆分大Key能對數據分片間的內存平衡起到顯著作用。
檢查是否使用了Hash Tag
如果使用了Hash Tag,請根據業務實際情況,評估將一個Hash Tag拆分為多個Hash Tag,使數據更加均勻地分布在不同的數據分片節點上。
擴容實例規格
擴容實例規格,可以增加實例每個分片的內存,是改善內存傾斜的臨時解決方案,具體操作請參見變更實例配置。
變配時實例會進行數據傾斜預檢查,若您選擇的實例規格無法解決內存傾斜問題,實例會進行攔截與報錯,請您調大實例規格后重試。
在成功升級實例規格后,會改善內存傾斜問題,但可能也引起帶寬傾斜或CPU傾斜。
附錄1:Redis內存占用介紹
Redis的內存占用主要由以下三部分組成:
內存占用 | 說明 |
鏈路內存(動態) | 主要包括Input Buff、Output Buff、JIT Overhead、Fake Lua Link、Lua執行緩存等,例如可執行INFO命令,通過返回結果的Clients中查看客戶端緩存信息。 說明 Input buff與Output buff與每個客戶端的連接有關,通常較小。當執行客戶端Range類操作或大Key收發較慢時,Input buff與Output buff占用的內存會增大,從而影響數據區,甚至會造成內存溢出OOM(Out Of Memory)。 |
數據內存 | 用戶數據區,即實際存儲的Value信息,通常作為重點分析的對象。 |
管理內存(靜態) | 啟動時較小且相對恒定,該區域由管理數據的Hash內存開銷、Repl-buff與aof-buff的內存開銷(約32 MB~64 MB)等構成。 說明 當Key數量特別多時(例如幾億個),會占用較大的內存。 |
大部分OOM場景是由于動態內存管理失效引起,例如限流時請求堆積導致動態內存快速上升、過于復雜或不合理的Lua腳本也可能導致OOM。Tair(企業版)增強了對動態內存的控制,推薦選用。
附錄2:查看內存占用情況的其他方法
使用MEMORY STATS命令查看內存占用情況
在Redis中,執行MEMORY STATS命令查詢內存使用詳情。
Redis實例的內存開銷主要由兩部分組成:
業務數據的內存開銷,該部分一般作為重點分析對象。
非業務數據的內存開銷,例如主備復制的積壓緩沖區、Redis進程初始化消耗的內存等。
返回示例及各參數對應的解釋如下:
下述返回信息中,內存數值的單位為字節。
1) "peak.allocated" //Redis進程自啟動以來消耗內存的峰值。
2) (integer) 79492312
3) "total.allocated" //Redis使用其分配器分配的總字節數,即當前的總內存使用量。
4) (integer) 79307776
5) "startup.allocated" //Redis啟動時消耗的初始內存量。
6) (integer) 45582592
7) "replication.backlog" //復制積壓緩沖區的大小。
8) (integer) 33554432
9) "clients.slaves" //主從復制中所有從節點的讀寫緩沖區大小。
10) (integer) 17266
11) "clients.normal" //除從節點外,所有其他客戶端的讀寫緩沖區大小。
12) (integer) 119102
13) "aof.buffer" //AOF持久化使用的緩存和AOF重寫時產生的緩存。
14) (integer) 0
15) "db.0" //業務數據庫的數量。
16) 1) "overhead.hashtable.main" //當前數據庫的hash鏈表開銷內存總和,即元數據內存。
2) (integer) 144
3) "overhead.hashtable.expires" //用于存儲key的過期時間所消耗的內存。
4) (integer) 0
17) "overhead.total" //數值=startup.allocated+replication.backlog+clients.slaves+clients.normal+aof.buffer+db.X。
18) (integer) 79273616
19) "keys.count" //當前Redis實例的key總數
20) (integer) 2
21) "keys.bytes-per-key" //當前Redis實例每個key的平均大小,計算公式:(total.allocated-startup.allocated)/keys.count。
22) (integer) 16862592
23) "dataset.bytes" //純業務數據占用的內存大小。
24) (integer) 34160
25) "dataset.percentage" //純業務數據占用的內存比例,計算公式:dataset.bytes*100/(total.allocated-startup.allocated)。
26) "0.1012892946600914"
27) "peak.percentage" //當前總內存與歷史峰值的比例,計算公式:total.allocated*100/peak.allocated。
28) "99.767860412597656"
29) "fragmentation" //內存的碎片率。
30) "0.45836541056632996"
使用MEMORY DOCTOR命令查看內存診斷建議
在Redis中,執行MEMORY DOCTOR命令獲取內存診斷建議。
MEMORY DOCTOR會從以下維度為Redis實例的提供內存診斷建議,您可以根據診斷建議制定相應的優化策略:
int empty = 0; /* Instance is empty or almost empty. */
int big_peak = 0; /* Memory peak is much larger than used mem. */
int high_frag = 0; /* High fragmentation. */
int high_alloc_frag = 0;/* High allocator fragmentation. */
int high_proc_rss = 0; /* High process rss overhead. */
int high_alloc_rss = 0; /* High rss overhead. */
int big_slave_buf = 0; /* Slave buffers are too big. */
int big_client_buf = 0; /* Client buffers are too big. */
int many_scripts = 0; /* Script cache has too many scripts. */
使用MEMORY USAGE命令查看指定Key占用的內存
在Redis中,執行MEMORY USAGE命令查詢指定Key消耗的內存(單位為字節)。
命令執行示例:
MEMORY USAGE Key0089393003
返回信息如下:
(integer) 1000072