Redis深入學習筆記(六)Redis內存分配
Redis的高效可以說是輕量級的epoll模型和基於內存的讀寫共同組成的,所以內存的使用就至關重要,本篇主要介紹Redis的內存分配原理。
獲取內存信息命令:info memory
used_memory: Redis分配器分配的內存總量,就是內部存儲的所有數據內存占用量。
used_memory_human: 以可讀的格式返回used_memory。
used_memory_rss: 以操作系統同的角度顯示Redis進程占用的物理內存總量。
used_memory_rss_human: 以可讀的格式返回used_memory_rss。
used_memory_peak: 內存使用的峰值。
used_memory_peak_human: 以可讀的格式返回used_memory_peak。
total_system_memory: 系統可用內存總量。
total_system_memory_human: 以可讀的格式返回total_system_memory。
used_memory_lua: lua引擎消耗的內存總量。
used_memory_lua_human: 以可讀的格式返回used_memory_lua。
maxmemory: Redis最大可用內存。
maxmemory_human: 以可讀的格式返回maxmemory。
maxmemory_policy: 達到最大可用內存時使用的淘汰策略。
mem_fragmentation_ratio: used_memory_rss/used_memory比值,標識碎片率。
mem_allocator: Redis使用的內存分配器,默認是jemalloc。
重要指標: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運行自身使用的內存
對象內存:用戶數據
緩沖內存:客戶端緩沖、復制積壓緩沖區、AOF緩沖區
客戶端緩沖:指的是所有接入到Redis服務器TCP連接的輸入輸出緩沖。輸入緩沖無法控制,最大空間為1G,如果超過將斷開連接。輸出緩沖通過參數client-output-buffer-limit控制。輸入輸出緩沖區在大流量的場景中容易失控,造成Redis內存的不穩定,需要重點監控。
普通客戶端:除了復制和訂閱的客戶端之外的所有連接,Redis的配置項是:client-output-buffer-limit。Redis並沒有對普通客戶端的輸出緩沖區做限制,一般普通客戶端的內存消耗可以忽略不計,但是當有大量慢連接客戶端接入時這部分內存消耗就不能忽略了,可以設置maxclients做限制。特別是當使用大量數據輸出的命令且數據無法及時推送給客戶端時,如monitor命令,容易造成Redis服務器內存突然飆升。
從客戶端:主節點會為每個從節點單獨建立一條連接用於命令復制,配置項是:client-output-buffer-limit。當主從節點之間網絡延遲較高或主節點掛載大量從節點時這部分內存消耗將占用很大一部分,建議主節點掛載的從節點不要多於2個,主從節點不要部署在較差的網絡環境下,如異地跨機房環境,防止復制客戶端連接緩慢造成溢出。
訂閱客戶端:當使用發布訂閱功能時,連接客戶端使用單獨的輸出緩沖區,配置項為:client-output-buffer-limit,當訂閱服務的消息生產快於消費速度時,輸出緩沖區會產生積壓造成輸出緩沖區空間溢出。
復制積壓緩沖區:Redis在2.8版本之後提供了一個可重用的固定大小緩沖區用於實現部分復制功能,根據repl-backlog-size參數控制,默認1MB。對於復制積壓緩沖區整個主節點只有一個,所有的從節點共享此緩沖區,因此可以設置較大的緩沖區空間,如100MB,這部分內存投入是有價值的,可以有效避免全量復制。
AOF緩沖區:這部分空間用於在Redis重寫期間保存最近的寫入命令。AOF緩沖區空間消耗用戶無法控制,消耗的內存取決於AOF重寫時間和寫入命令量,這部分空間占用通常很小。
內存碎片:
Redis默認的內存分配器采用jemalloc,在64位系統中將內存空間劃分為:小、大、巨大三個範圍。每個範圍內又劃分為多個小的內存塊單位,如下所示:
小:[8byte],[16byte,32byte,48byte,...,128byte],[192byte,428256byte,...,512byte],[768byte,1024byte,...,3840byte]
大:[4KB,8KB,12KB,...,4072KB]
巨大:[4MB,8MB,12MB,...]
大量過期鍵刪除,鍵對象過期刪除後,釋放的空間無法得到充分利用,導致碎片率上升。重啟節點可以做到內存碎片重新整理,因此可以利用高可用架構,如Sentinel或Cluster,將碎片率過高的主節點轉換為從節點,進行安全重啟。
子進程內存消耗:
子進程內存消耗主要指執行AOF/RDB重寫時Redis創建的子進程內存消耗。Redis執行fork操作產生的子進程內存占用量對外表現為與父進程相同,理論上需要一倍的物理內存來完成重寫操作。但Linux具有寫時復制技術(copy-on-write),父子進程會共享相同的物理內存頁,當父進程處理寫請求時會對需要修改的頁復制出一份副本完成寫操作,而子進程依然讀取fork時整個父進程的內存快照。
Linux Kernel在2.6.38內核增加了Transparent Huge Pages(THP)機制,而有些Linux發行版即使內核達不到2.6.38也會默認加入並開啟這個功能,如Redhat Enterprise Linux在6.0以上版本默認會引入THP。雖然開啟THP可以降低fork子進程的速度,但之後copy-on-write期間復制內存頁的單位從4KB變為2MB,如果父進程有大量寫命令,會加重內存拷貝量,從而造成過度內存消耗。
子進程內存消耗需要註意:
Redis產生的子進程並不需要消耗1倍的父進程內存,實際消耗根據期間寫入命令量決定,但是依然要預留出一些內存防止溢出。
需要設置sysctl vm.overcommit_memory=1允許內核可以分配所有的物理內存,防止Redis進程執行fork時因系統剩余內存不足而失敗。
排查當前系統是否支持並開啟THP,如果開啟建議關閉,防止copy-on-write期間內存過度消耗。
下一篇會介紹Redis不同類型對象的存儲和管理
Redis深入學習筆記(六)Redis內存分配