前言
redis 是一個記憶體型資料庫,那麼就需要重點關注一下記憶體了。
正文
了解Redis記憶體,首先需要掌握Redis記憶體消耗在哪些方面。有些記憶體消 耗是必不可少的,而有些可以通過參數調整和合理使用來規避記憶體浪費。
記憶體消耗可以分為程序自身消耗和子程序消耗。
首先需要了解Redis自身使用記憶體的統計資料,可通過執行info memory 指令擷取記憶體相關名額。

需要重點關注的名額有:used_memory_rss和used_memory以及它們的比 值mem_fragmentation_ratio。
當mem_fragmentation_ratio>1時,說明used_memory_rss-used_memory多出 的部分記憶體并沒有用于資料存儲,而是被記憶體碎片所消耗,如果兩者相差很大,說明碎片率嚴重。
當mem_fragmentation_ratio<1時,這種情況一般出現在作業系統把Redis 記憶體交換(Swap)到硬碟導緻,出現這種情況時要格外關注,由于硬碟速 度遠遠慢于記憶體,Redis性能會變得很差,甚至僵死。
記憶體消耗
記憶體消耗劃分
Redis程序内消耗主要包括:自身記憶體+對象記憶體+緩沖記憶體+記憶體碎片, 其中Redis空程序自身記憶體消耗非常少,通常used_memory_rss在3MB左右, used_memory在800KB左右,一個空的Redis程序消耗記憶體可以忽略不計。
對象記憶體是Redis記憶體占用最大的一塊,存儲着使用者所有的資料。Redis 所有的資料都采用key-value資料類型,每次建立鍵值對時,至少建立兩個類 型對象:key對象和value對象。對象記憶體消耗可以簡單了解為sizeof(keys) +sizeof(values)。鍵對象都是字元串,在使用Redis時很容易忽略鍵對記憶體 消耗的影響,應當避免使用過長的鍵。value對象更複雜些,主要包含5種基本資料類型:字元串、清單、哈希、集合、有序集合。
緩沖記憶體
緩沖記憶體主要包括:用戶端緩沖、複制積壓緩沖區、AOF緩沖區。
用戶端緩沖指的是所有接入到Redis伺服器TCP連接配接的輸入輸出緩沖。 輸入緩沖無法控制,最大空間為1G,如果超過将斷開連接配接。
普通用戶端:
除了複制和訂閱的用戶端之外的所有連接配接,Redis的預設 配置是:client-output-buffer-limit normal000,Redis并沒有對普通用戶端的輸 出緩沖區做限制,一般普通用戶端的記憶體消耗可以忽略不計,但是當有大量 慢連接配接用戶端接入時這部分記憶體消耗就不能忽略了,可以設定maxclients做 限制。特别是當使用大量資料輸出的指令且資料無法及時推送給用戶端時, 如monitor指令,容易造成Redis伺服器記憶體突然飙升。
從用戶端:
主節點會為每個從節點單獨建立一條連接配接用于指令複制, 預設配置是:client-output-buffer-limit slave256mb64mb60。當主從節點之間 網絡延遲較高或主節點挂載大量從節點時這部分記憶體消耗将占用很大一部 分,建議主節點挂載的從節點不要多于2個,主從節點不要部署在較差的網 絡環境下,如異地跨機房環境,防止複制用戶端連接配接緩慢造成溢出。
訂閱用戶端:
訂閱用戶端:當使用釋出訂閱功能時,連接配接用戶端使用單獨的輸出緩 沖區,預設配置為:client-output-buffer-limit pubsub32mb8mb60,當訂閱服務 的消息生産快于消費速度時,輸出緩沖區會産生積壓造成輸出緩沖區空間溢 出
複制積壓緩沖區:Redis在2.8版本之後提供了一個可重用的固定大小緩 沖區用于實作部分複制功能,根據repl-backlog-size參數控制,預設1MB。對 于複制積壓緩沖區整個主節點隻有一個,所有的從節點共享此緩沖區,是以 可以設定較大的緩沖區空間,如100MB,這部分記憶體投入是有價值的,可以 有效避免全量複制
AOF緩沖區:這部分空間用于在Redis重寫期間儲存最近的寫入指令。AOF緩沖區空間消耗使用者無法控制,消耗的記憶體取決于 AOF重寫時間和寫入指令量,這部分空間占用通常很小。
記憶體碎片
Redis預設的記憶體配置設定器采用jemalloc,可選的配置設定器還有:glibc、 tcmalloc。記憶體配置設定器為了更好地管理和重複利用記憶體,配置設定記憶體政策一般 采用固定範圍的記憶體塊進行配置設定。例如jemalloc在64位系統中将記憶體空間劃 分為:小、大、巨大三個範圍。每個範圍内又劃分為多個小的記憶體塊機關, 如下所示:
比如當儲存5KB對象時jemalloc可能會采用8KB的塊存儲,而剩下的3KB 空間變為了記憶體碎片不能再配置設定給其他對象存儲。記憶體碎片問題雖然是所有 記憶體服務的通病,但是jemalloc針對碎片化問題專門做了優化,一般不會存 在過度碎片化的問題,正常的碎片率(mem_fragmentation_ratio)在1.03左 右。但是當存儲的資料長短差異較大時,以下場景容易出現高記憶體碎片問題:
- ·頻繁做更新操作,例如頻繁對已存在的鍵執行append、setrange等更新 操作。
- ·大量過期鍵删除,鍵對象過期删除後,釋放的空間無法得到充分利 用,導緻碎片率上升。、
解決手法:
- ·資料對齊:在條件允許的情況下盡量做資料對齊,比如資料盡量采用 數字類型或者固定長度字元串等,但是這要視具體的業務而定,有些場景無 法做到
- ·安全重新開機:重新開機節點可以做到記憶體碎片重新整理,是以可以利用高可用架構,如Sentinel或Cluster,将碎片率過高的主節點轉換為從節點,進行 安全重新開機。
記憶體回收政策
Redis的記憶體回收機制主要展現在以下兩個方面:
- 删除到達過期時間的鍵對象。
Redis所有的鍵都可以設定過期屬性,内部儲存在過期字典中。由于進 程内儲存大量的鍵,維護每個鍵精準的過期删除機制會導緻消耗大量的 CPU,對于單線程的Redis來說成本過高,是以Redis采用惰性删除和定時任 務删除機制實作過期鍵的記憶體回收。
a. 惰性删除
惰性删除用于當用戶端讀取帶有逾時屬性的鍵時,如果已 經超過鍵設定的過期時間,會執行删除操作并傳回空,這種政策是出于節省 CPU成本考慮,不需要單獨維護TTL連結清單來處理過期鍵的删除。但是單獨用 這種方式存在記憶體洩露的問題,當過期鍵一直沒有通路将無法得到及時删 除,進而導緻記憶體不能及時釋放。正因為如此,Redis還提供另一種定時任 務删除機制作為惰性删除的補充。
b. 定時任務删除
Redis内部維護一個定時任務,預設每秒運作10次(通 過配置hz控制)。定時任務中删除過期鍵邏輯采用了自适應算法,根據鍵的 過期比例、使用快慢兩種速率模式回收鍵
1)定時任務在每個資料庫空間随機檢查20個鍵,當發現過期時删除對 應的鍵。
2)如果超過檢查數25%的鍵過期,循環執行回收邏輯直到不足25%或 運作逾時為止,慢模式下逾時時間為25毫秒。
3)如果之前回收鍵邏輯逾時,則在Redis觸發内部事件之前再次以快模 式運作回收過期鍵任務,快模式下逾時時間為1毫秒且2秒内隻能運作1次。
4)快慢兩種模式内部删除邏輯相同,隻是執行的逾時時間不同。
- 記憶體使用達到maxmemory上限時觸發記憶體溢出控制政策。
記憶體溢出控制政策
當Redis所用記憶體達到maxmemory上限時會觸發相應的溢出控制政策。 具體政策受maxmemory-policy參數控制,Redis支援6種政策,如下所示:
1)noeviction:預設政策,不會删除任何資料,拒絕所有寫入操作并返 回用戶端錯誤資訊(error)OOM command not allowed when used memory,此 時Redis隻響應讀操作。
2)volatile-lru:根據LRU算法删除設定了逾時屬性(expire)的鍵,直 到騰出足夠空間為止。如果沒有可删除的鍵對象,回退到noeviction政策。
3)allkeys-lru:根據LRU算法删除鍵,不管資料有沒有設定逾時屬性, 直到騰出足夠空間為止。
4)allkeys-random:随機删除所有鍵,直到騰出足夠空間為止。
5)volatile-random:随機删除過期鍵,直到騰出足夠空間為止。
6)volatile-ttl:根據鍵值對象的ttl屬性,删除最近将要過期資料。如果 沒有,回退到noeviction政策。
記憶體溢出控制政策可以采用config set maxmemory-policy{policy}動态配 置。Redis支援豐富的記憶體溢出應對政策,可以根據實際需求靈活定制,比如當設定volatile-lru政策時,
保證具有過期屬性的鍵可以根據LRU剔除,而 未設定逾時的鍵可以永久保留。還可以采用allkeys-lru政策把Redis變為純緩 存伺服器使用。當Redis因為記憶體溢出删除鍵時,可以通過執行info stats指令 檢視evicted_keys名額找出目前Redis伺服器已剔除的鍵數量。