【死磕 Redis】------ 理解 Redis 的內存

我們知道 Redis 的所有數據都存儲在內存中,內存是我們系統中的一個非常珍貴的資源,不能隨意浪費,所以如何合理高效地利用 Redis 內存就變得非常重要了。本文從兩個方面來闡述 Redis 的內存機制:

  1. 知道 Redis 的內存主要消耗在什麼地方

  2. 如何管理內存

查看內存

在文章【死磕 Redis】----- info 命令詳解介紹了 info memory 命令可以查看 Redis 內存消耗情況,是我們分析 Redis 內存使用情況的好工具。執行命令後如下:

我們重點關注幾個指標:

屬性名屬性說明
used_memoryRedis 分配器分配的內存總量,指 Redis 存儲的所有數據所佔的內存
used_memory_human以可讀的形式返回 user_memory
used_memory_rssRedis 進程佔用的物理內存總量
used_memory_peakused_memory 使用的峯值
used_memory_peak_human可讀格式返回 usedmemorypeak
used_memory_luaLua 引擎消耗的內存大小
mem_fragmentation_ratiousedmemoryrss/used_memory 比值,內存碎片率
mem_allocatorRedis 所使用的內存分配器,默認 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 回收內存大致有兩種機制:

  1. 刪除達到過期時間的對象

  2. 當內存達到 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】----- Redis 通信協議 RESP

【死磕 Redis】----- Redis 的線程模型

【死磕 Redis】----- 事務

【死磕 Redis】----- 理解 pipeline 管道

【死磕 Redis】----- 布隆過濾器

【死磕 Redis】----- 發佈與訂閱

【死磕 Redis】-----如何排查 Redis 中的慢查詢

【死磕 Redis】-----持久化

【死磕 Redis】----- 主從複製(一):概述

【死磕 Redis】----- 主從複製(二):全量複製和部分複製

【死磕 Redis】----- 主從複製(三):注意的問題

【死磕 Redis】----- 哨兵(一):部署哨兵架構

【死磕 Redis】----- 哨兵(二):基本原理

【死磕 Redis】----- info 命令詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章