1. 程式人生 > >「Nosql」Redis小記-內存解析&內存消耗篇

「Nosql」Redis小記-內存解析&內存消耗篇

產生 對象內存 節點 解決 redis啟動 碎片化 依然 sysctl 分配

*博客搬家:初版發布於 2017/08/12 18:32 原博客地址:https://my.oschina.net/sunqinwen/blog/1507171

Redis內存消耗分析

註:本文默認讀者已初步學會使用redis了。

首先我們通過info命令查看相關指標,其中幾個memory的重要指標整理出來如下:

屬性 解釋
used_memory redis內部存儲的所有數據的內存總占用量(自身內存+對象內存+緩沖內存)
used_memory_ress redis進程占用的總物理內存
mem_fragmentation_ratio used_memory_ress與used_memory的比值,即為內存碎片率
mem_allocator 內存分配器,默認為jemalloc

(1)碎片率的解釋

內存碎片率>1時,說明redis進程占用物理內存的總量大於Redis實際存儲數據(表1-1第一行)的內存占用量,溢出來的部分內存被內存碎片消耗,如果溢出部分過大,則說明內存碎片率嚴重。

相反的,如果碎片率<1時,則說明Redis存儲的數據總量已經超出了redis進程占用內存的總量,造成這種情況是因為操作系統把Redis內存交換至硬盤導致(swap),由於硬盤讀取速度遠遠慢與內存,因此這種情況下redis性能極差,可能出現僵死。

(2)redis內存消耗的幾個來源

自身內存:redis啟動後自身運行所需內存;

對象內存:內存占用最大的一部分,這裏面存儲的就是用戶自身的數據(業務數據),數據以key-value類型存儲,內存消耗可表示為:key內存+value內存。

緩沖內存:主要由客戶端緩沖區+復制積壓緩沖區+AOF緩沖區組成,具體解釋如下:

  • 客戶端緩沖區指的是所有接入redis服務器的TCP連接的輸入和輸出緩沖,輸入緩沖無法被控制,最大空間為1G,超過立即斷開連接,輸出緩沖通過client-output-buffer-limit控制。
  • 復制積壓緩沖區指的是redis在2.8版本以後提供了一塊可以重復利用的固定大小的緩沖區,用來實現部分復制功能,使用repl-backlog-size參數控制,默認1MB(主從結構下,主節點只存在一個該緩沖區,從節點共用,那時可以設置較大的緩沖區空間),該緩沖區可以避免全量復制。
  • AOF緩沖區用於存儲在redis重寫期間保存最近的寫入命令,無法控制,通常取決於AOF重寫時間以及寫入命令量,一般情況下很小。

內存碎片:redis默認的內存分配器是jemalloc,可選的還有glibc和tcmalloc;內存分配器為了更好的管理以及重復利用內存,分配策略一般采用固定範圍的內存塊進行分配;因此,我們在存儲一塊5kb的內容時,內存分配器可能會為我們分配8kb的塊存儲,剩下的3kb不能再次分配給其他對象存儲,因而淪為了內存碎片;jemalloc對碎片化問題做了優化,一般來講碎片化率保持在1.03左右。

可能造成內存碎片率過高的場景:

  • 頻繁的更新操作,例如頻繁對已存在的鍵做append、setrange等操作;
  • 大量過期鍵刪除,鍵對象過期刪除後釋放的空間無法得到充分的利用,導致碎片率上升。

解決辦法:

  • 數據對齊,盡量采用數字類型或固定長度的字符串(大部分業務場景不滿足這種方式);
  • 重啟,重啟節點可以使內存重整理,利用高可用的結構(節點集群+主從結構),將碎片率過高的節點主節點轉換為從節點,然後進行安全重啟。

子進程內存消耗:子進程內存消耗指的是執行AOF/RDB重寫時redis創建的子進程內存消耗;redis執行fork操作產生的子進程內存占用量對外表現為與父進程相同,理論上需要一倍的物理內存來完成重寫的操作。但是linux具備寫時復制技術(copy-on-write),父子進程會共享相同的物理內存頁,當父進程處理寫請求時會對需要修改的頁復制出一份副本來完成寫操作,而子進程依然讀取fork時整個父進程的內存快照,總結:

  • 子進程並不需要消耗一倍的父進程內存,實際消耗根據期間寫入命令量決定,但依然要預留出一些內存防止溢出;
  • 需要設置sysctl vm.overcommit_memory=1允許內核可以分配所有的物理內存,防止redis進程執行fork時因剩余內存不足導致失敗;
  • 排查當前系統是否支持開啟THP,如果開啟建議關閉,防止copy-on-write期間內存過度消耗。

「Nosql」Redis小記-內存解析&內存消耗篇