我們知道 Redis 的所有數據都存儲在內存中,內存是我們系統中的一個非常珍貴的資源,不能隨意浪費,所以如何合理高效地利用 Redis 內存就變得非常重要了。本文從兩個方面來闡述 Redis 的內存機制:
知道 Redis 的內存主要消耗在什麼地方
如何管理內存
查看內存
在文章【死磕 Redis】----- info 命令詳解介紹了 info memory
命令可以查看 Redis 內存消耗情況,是我們分析 Redis 內存使用情況的好工具。執行命令後如下:
我們重點關注幾個指標:
屬性名 | 屬性說明 |
---|---|
used_memory | Redis 分配器分配的內存總量,指 Redis 存儲的所有數據所佔的內存 |
used_memory_human | 以可讀的形式返回 user_memory |
used_memory_rss | Redis 進程佔用的物理內存總量 |
used_memory_peak | used_memory 使用的峯值 |
used_memory_peak_human | 可讀格式返回 usedmemorypeak |
used_memory_lua | Lua 引擎消耗的內存大小 |
mem_fragmentation_ratio | usedmemoryrss/used_memory 比值,內存碎片率 |
mem_allocator | Redis 所使用的內存分配器,默認 jemalloc |
這裏我們需要重點關注
mem_fragmentation_ratio>1
說明多出來的部分名沒有用於數據存儲,而是被內存碎片所消耗,相差越大,說明內存碎片率越嚴重。mem_fragmentation_ratio<1
一般出現在Redis內存交換(Swap)到硬盤導致(used_memory>可用最大內存時
,Redis會把舊的和不適用的數據寫入到硬盤,這塊空間就叫Swap空間),出現這種情況需要格外關注,硬盤速度遠遠慢於內存,Redis性能就會變得很差,甚至僵死。在理想情況下mem_fragmentation_ratio
只會比 1 稍微大一點點,也就是 usedmemoryrss 的值應該只比 used_memory 稍微高一些。
內存消耗劃分
Redis 的內存主要包括:對象內存 + 緩衝內存 + 自身內存 + 內存碎片。如下
對象內存
對象內存是 Redis 內存中佔用最大的一塊,存儲着所有的用戶數據。我們知道 Redis 是一個 key-value 的內存數據庫,所有的數據都採用 key-value 型數據類型,每次在創建 key-value 鍵值對對象的時候都要創建兩個對象:key 對象和value 對象。其中 key 對象是字符串,value 對象我們知道有五中數據類型-String、Hash、List、Set、Zset,每種數據類型在使用的時候佔用的內存不同。
緩衝內存
主要包括:客戶端緩衝、AOF 緩衝區、複製積壓緩衝區。
客戶端緩衝:普通的客戶端連接
AOF 緩衝區:Redis 持久化分爲兩種:RDB 和 AOF,其中 RDB 是內存快照,AOF 是將 Redis 的命令 append 在文件中,不過在寫入文件之前會先寫入到緩衝區,然後根據不同的持久化策略向磁盤進行同步。在進行 AOF 重寫時也有一個AOF 重寫緩衝區。一般 AOF 緩衝區都會比較小。
複製積壓緩衝區:主要用於主從同步。在進行主從同步時,Redis 會將最新的命令寫入到複製積壓緩衝區,在進行復制的時候,會校驗複製偏移量是否在複製積壓緩衝區中,如果是則進行部分複製,否則進行全量複製。它默認情況下是 1MB,我們需要根據實際請求適當調整他的大小,畢竟設置太小的話,可能會使部分複製退化爲全量複製。
自身內存
自身內存主要指 AOF/RDB 的時候 Redis 創建子進程內存的消耗,一般這部分的消耗會比較小。
內存碎片
目前可選的分配器有 jemalloc、glibc、tcmalloc,默認 jemalloc。
出現高內存碎片問題的情況:大量的更新操作,比如 append、setrange;大量的過期鍵刪除,釋放的空間無法得到有效利用。
解決辦法:數據對齊,安全重啓(高可用/主從切換)。
內存管理
設置 maxmemory
如果我們不設置 maxmemory ,Redis 則默認使用無限內存,所以爲了 Redis 不繫統的內存耗盡,我們在使用 Redis 的時候儘量去配置 maxmemory,給 Redis 設置內存使用上限。maxmemory 配置的是 Redis 的實際使用內存,即 usedmemory,但是由於有內存碎片的存在,所以 Redis 實際使用的內存會比 usedmemory 要大,在合理情況下一般只會大一點點。
配置內存回收策略
Redis 回收內存大致有兩種機制:
刪除達到過期時間的對象
當內存達到 maxmemory 時觸發內存溢出控制策略,強制刪除選擇出來的對象
Redis 刪除過期鍵值對對象一般有兩種策略:惰性刪除和主動定時任務刪除。
惰性刪除:這種刪除策略,Redis 不會主動去刪除已經過期的鍵值對,而是等待客戶端去讀取帶有超時屬性的鍵時,如果已經超時了則刪除該鍵值對對象,然後返回空。這樣有一個好處就是節省了 CPU ,因爲 Redis 不需要單獨去維護 TTL 鏈表來處理過期鍵的刪除,但是有一個壞處就是如果過期的鍵一直都沒有被訪問,則永遠不會被刪除了。那麼怎麼解決呢?Redis 提供了一個定時任務的刪除機制來補救。
定時任務刪除:Redis 內部維護一個定時任務,默認是每秒運行 10 次,刪除邏輯如下圖:
內存溢出控制策略
當 Redis 所用內存達到 maxmemory 上限時會觸發相應的溢出控制策略。Redis支持6種策略,如下所示:
策略 | 說明 |
---|---|
noeviction | 默認策略,不會刪除任何數據,拒絕所有寫入操作並返 回客戶端錯誤信息(error)OOM command not allowed when used memory,此 時Redis只響應讀操作。 |
volatile-lru | 根據LRU算法刪除設置了超時屬性(expire)的鍵,直 到騰出足夠空間爲止。如果沒有可刪除的鍵對象,回退到noeviction策略。 |
allkeys-lru | 根據LRU算法刪除鍵,不管數據有沒有設置超時屬性, 直到騰出足夠空間爲止。 |
allkeys-random | 隨機刪除所有鍵,直到騰出足夠空間爲止。 |
volatile-random | 隨機刪除過期鍵,直到騰出足夠空間爲止。 |
volatile-ttl | 根據鍵值對象的ttl屬性,刪除最近將要過期數據。如果沒有,回退到noeviction策略。 |
內存溢出控制策略可以使用 configsetmaxmemory-policy{policy}
語句進行動態配置。
當 Redis 因爲內存溢出刪除鍵時,可以通過執行 info stats
命令查看 evicted_keys
指標找出當前 Redis 服務器已剔除的鍵數量。
參考
《Redis 開發與運維》
理解Redis的內存
往
期
精
彩
【死磕 Redis】----- Redis 通信協議 RESP
【死磕 Redis】----- 理解 pipeline 管道
【死磕 Redis】-----如何排查 Redis 中的慢查詢