1. 程式人生 > >Redis之記憶體分析

Redis之記憶體分析

一 記憶體消耗

1.1 記憶體使用統計

 

used_memory: Redis分配的記憶體總量,即儲存的所有資料佔用的記憶體

used_memory_human: 以可讀格式返回使用的記憶體量

used_memory_rss:從系統角度,顯示Redis程序佔用的實體記憶體總量

used_memory_rss_human:以可讀格式返回Redis程序佔用的實體記憶體總量

used_memory_peak:記憶體使用的最大值,表示used_memory峰值

used_memory_peak_human:以可讀格式返回記憶體使用的最大值

total_system_memory:系統總記憶體

total_system_memory_human:以可讀格式返回系統總記憶體

used_memory_lua:Lua程序使用記憶體

used_memory_lua_human:以可讀格式返回Lua程序使用記憶體

mem_fragmentation_ratio:記憶體碎片率,等價於(used_memory_rss /used_memory)

mem_allocator:redis使用的記憶體分配器

重點關注used_memory以及used_memory_rss和碎片率mem_fragmentation_ratio

mem_fragmentation_ratio > 1: used_memory_rss -used_memory 多出部分沒有儲存資料,而是被記憶體碎片消耗了,如果差的很大,說明碎片很嚴重

mem_fragmentation_ratio < 1: 一般出現在作業系統把Redis記憶體交換到硬碟導致。

 

1.2 記憶體消耗劃分

Redis程序內的記憶體主要包括:

自身記憶體 + 物件記憶體 + 緩衝記憶體+ 記憶體碎片,其中Redis自身記憶體消耗的很少,通常used_memory_rss在3MB左右,used_memory在800k左右。

 

1.2.1 物件記憶體

物件記憶體是Redis中最佔記憶體的一塊,儲存著使用者所有資料。Redis所有資料都採用key-value,每次你建立key-value,都是建立2個物件,即key物件和value物件,即 key的size + value的size. Key物件都是字串,我們應當避免使用過長的key. Value物件根據使用情況不同佔用記憶體也不盡相同,注意監控。

 

1.2.2 緩衝記憶體

緩衝記憶體涉及到客戶端緩衝區,複製積壓緩衝區和AOF緩衝區

客戶端緩衝:指的是所有連線到Redis的伺服器tcp連線輸入輸出緩衝,輸入緩衝無法控制,最大空間1G;輸出緩衝可通過client-output-buffer-limit控制。

# 普通客戶端:client-output-buffer-limit normal 0 0 0

普通客戶端預設並沒有對輸出緩衝區做限制。但是如果當有大量的慢連線客戶端接入時,這部分消耗就不能忽略了,因為消費的很慢,在成輸出緩衝區資料積壓。所以可以設定maxclients做限制。

# 從客戶端:client-output-buffer-limit slave 256mb 64mb 60

主節點會每一個從節點單獨建立一條連線用於命令複製。當主節點網路延遲較高或主節點掛載大量的從節點時,這部分記憶體消耗將佔用很大一部分,建議主節點掛載從節點最好不要超過2個。

# 訂閱客戶端:client-output-buffer-limit pubsub 32mb 8mb 60 當生產訊息的速度快於消費的速度時,輸出緩衝區容易積壓訊息

 

複製積壓緩衝區:一個可重用的固定大小緩衝區用於實現部分複製功能,根據repl-backlog-size引數控制,預設1MB. 對於複製積壓緩區,主節點有一個,所有從節點啊共享這個緩衝區,因此可以設定較大的值,比如100MB,這部分投入是有價值的,可以有效避免全量複製。

 

AOF緩衝區:用於AOF重寫期間儲存最近寫入的命令,等待被刷到磁碟

 

1.2.3 記憶體碎片

Redis預設的記憶體分配器是jemalloc,記憶體分配器的作用就是為了更好的管理和重複利用記憶體。但是當儲存的資料長短差異較大的時候,以下場景容易出現高記憶體碎片問題:

# 頻繁更新,對已經存在的key進行append setrange操作

# 大量過期鍵刪除,鍵物件過期刪除後,釋放的 空間無法得到拆分利用

解決辦法:

# 儘量資料對齊,視業務情況而定

# 安全重啟:重啟可以做到記憶體碎片重新整理

 

1.3 子程序記憶體消耗

子程序記憶體主要是指AOF/RDB重寫時Redis建立的子程序記憶體消耗。Redis執行fork操作產生子程序記憶體佔用對外表現為與父程序相同,理論上需要一倍的相同實體記憶體來完成重寫操作。但是Linux的copy-on-write機制使得父子程序共享相同的實體記憶體頁,當父程序處理寫請求時會對需要修改的頁複製出一份副本完成寫操作,而子程序依舊讀取fork時整個父程序記憶體快照。

# Redis子程序並不需要消耗1倍 的父程序記憶體,但是依然要預留一些記憶體防止記憶體溢位

# 需要設定sysctl vm.overcommit_memory=1允許核心可以分配所有的實體記憶體,防止Redis程序執行fork時因系統剩餘記憶體不足而失敗。

# 排查當前系統是否支援並開啟THP,如果開啟,建議關閉。

 

 

二 記憶體管理

2.1 設定記憶體上限

Redis通過maxmemory引數限制最大可用記憶體。限制記憶體目的主要有:

# 用於快取場景,當超出記憶體上限maxmemory時候使用LRU等刪除策略釋放空間

# 防止所用記憶體超過伺服器實體記憶體

 

需要注意的是,maxmeory限制的是Redis實際使用的記憶體量,也就是used_memory統計項對應的記憶體。通過設定上限,可以方便實現一臺伺服器部署多個Redis程序的記憶體控制。比如一臺32G的記憶體的伺服器,預留4GB記憶體給系統,預留4GB給Redis fork程序,留給Redis24GB記憶體,這樣就可以部署3個maxmemory=8GB的redis程序。

 

2.2 動態調整記憶體上限

可以通過命令config set maxmemory8GB

 

2.3 記憶體回收策略

記憶體回收機制主要體現在以下兩個方面:

2.3.1刪除過期的key

Redis採用惰性刪除和定時任務刪除機制實現過期key的記憶體回收

# 惰性刪除

用於當客戶端讀取帶有超時屬性的key的時候,如果已經超過設定的過期時間,會執行刪除操作並返回空。但是有一個問題,當過期鍵一直沒有訪問將無法得到及時刪除,從而導致記憶體 不能及時釋放

# 定時任務刪除

Redis內部維護一個定時任務,預設每秒執行10次,通過配置hz屬性控制。

 

2.3.2 記憶體使用達到maxmeory上限時觸發記憶體溢位的控制策略

當Redis所用記憶體達到maxmeory上限時,會觸發相應的溢位控制策略。具體策略受maxmeory-policy引數控制,Redis支援6種策略:

 

# volatile-lru -> 根據LRU演算法刪除設定了超時屬性的鍵,直到騰出足夠空間為止

# allkeys-lru -> 根據LRU演算法刪除鍵,不管有沒有設定超時屬性,直到騰出足夠空間為止

# volatile-random -> 隨即刪除過期鍵,直到騰出足夠空間為止

# allkeys-random -> 隨即刪除所有鍵,直到騰出足夠空間為止

# volatile-ttl -> 根據ttl屬性,刪除最近將要過期的資料,如果沒有回退到noeviction策略

# noeviction -> 不會刪除任何資料,拒絕所有寫入操作,並返回錯誤資訊,此時只是響應讀

 

三 記憶體優化

3.1 redisObject物件

 

3.2 縮減鍵值物件

降低Redis記憶體使用最直接的方式就是縮減key和value的長度

# key的設計:越短越好,如user:{userid}:friends:notify:{fid},可以簡化為u:{uid}:fs:nt:{fid}

# value: 值物件縮減比較複雜,常見的需求是把業務物件序列化放入Redis。

 

3.3 共享物件池

 

3.4 字串優化

3.5 編碼優化

3.6 控制鍵的數量

比如通過hash資料結構就可以減少key的數量,從而減少了RTT的時間,還可以減少key,這樣也節約了記憶體