一文瞭解 Redis 記憶體監控和記憶體消耗

来源:https://www.cnblogs.com/remcarpediem/archive/2019/10/16/11687524.html
-Advertisement-
Play Games

Redis 是一種記憶體資料庫,將數據保存在記憶體中,讀寫效率要比傳統的將數據保存在磁碟上的資料庫要快很多。所以,監控 Redis 的記憶體消耗並瞭解 Redis 記憶體模型對高效並長期穩定使用 Redis 至關重要。 ...


Redis 是一種記憶體資料庫,將數據保存在記憶體中,讀寫效率要比傳統的將數據保存在磁碟上的資料庫要快很多。所以,監控 Redis 的記憶體消耗並瞭解 Redis 記憶體模型對高效並長期穩定使用 Redis 至關重要。

記憶體使用統計

通過 info memory 命令可以獲得 Redis 記憶體相關的指標。較為重要的指標和解釋如下所示:

屬性名屬性說明
used_memory Redis 分配器分配的記憶體總量,也就是內部存儲的所有數據記憶體占用量
usedmemoryhuman 以可讀的格式返回 used_memory
usedmemoryrss 從操作系統的角度顯示 Redis 進程占用的物理記憶體總量
usedmemoryrss_human usedmemoryrss 的用戶宜讀格式的顯示
usedmemorypeak 記憶體使用的最大值,表示 used_memory 的峰值
usedmemorypeak_human 以可讀的格式返回 usedmemorypeak的值
usedmemorylua Lua 引擎所消耗的記憶體大小。
memfragmentationratio usedmemoryrss / used_memory 的比值,可以代表記憶體碎片率
maxmemory Redis 能夠使用的最大記憶體上限,0表示沒有限制,以位元組為單位。
maxmemory_policy Redis 使用的記憶體回收策略,可以是 noeviction、allkeys-lru、volatile-lru、allkeys-random、volatile-random 或者 volatile-ttl。預設是noeviction,也就是不會回收。

image.png

當 memfragmentationratio > 1 時,說明有部分記憶體並沒有用於數據存儲,而是被記憶體碎片所消耗,如果該值很大,說明碎片率嚴重。 當 memfragmentationratio < 1 時,這種情況一般出現在操作系統把 Redis 記憶體交換 (swap) 到硬碟導致,出現這種情況要格外關註,由於硬碟速度遠遠慢於記憶體,Redis 性能會變得很差,甚至僵死。

當 Redis 記憶體超出可以獲得記憶體時,操作系統會進行 swap,將舊的頁寫入硬碟。從硬碟讀寫大概比從記憶體讀寫要慢5個數量級。used_memory 指標可以幫助判斷 Redis 是否有被swap的風險或者它已經被swap。

在 Redis Administration 一文 (鏈接在文末) 建議要設置和記憶體一樣大小的交換區,如果沒有交換區,一旦 Redis 突然需要的記憶體大於當前操作系統可用記憶體時,Redis 會因為 out of memory 而被 Linix Kernel 的 OOM Killer 直接殺死。雖然當 Redis 的數據被換出 (swap out) 時,Redis的性能會變差,但是總比直接被殺死的好。

Redis 使用 maxmemory 參數限制最大可用記憶體。限制記憶體的目的主要有:

  • 用於緩存場景,當超出記憶體上限 maxmemory 時使用 LRU 等刪除策略釋放空間。
  • 防止所用的記憶體超過伺服器物理記憶體,導致 OOM 後進程被系統殺死。

maxmemory 限制的是 Redis 實際使用的記憶體量,也就是 used_memory 統計項對應的記憶體。實際消耗的記憶體可能會比 maxmemory 設置的大,要小心因為這部記憶體導致 OOM。所以,如果你有 10GB 的記憶體,最好將 maxmemory 設置為 8 或者 9G

記憶體消耗劃分

Redis 進程內消耗主要包括:自身記憶體 + 對象記憶體 + 緩衝記憶體 + 記憶體碎片,其中 Redis 空進程自身記憶體消耗非常少,通常 usedmemoryrss 在 3MB 左右時,used_memory 一般在 800KB 左右,一個空的 Redis 進程消耗記憶體可以忽略不計。

對象記憶體

對象記憶體是 Redis 記憶體占用最大的一塊,存儲著用戶所有的數據。Redis 所有的數據都採用 key-value 數據類型,每次創建鍵值對時,至少創建兩個類型對象:key 對象和 value 對象。對象記憶體消耗可以簡單理解為這兩個對象的記憶體消耗之和(還有類似過期之類的信息)。鍵對象都是字元串,在使用 Redis 時很容易忽略鍵對記憶體消耗的影響,應當避免使用過長的鍵。有關 Redis 對象系統的詳細內容,請看我之前的文章十二張圖帶你瞭解 Redis 的數據結構和對象系統

緩衝記憶體

緩衝記憶體主要包括:客戶端緩衝、複製積壓緩衝區和 AOF 緩衝區。

客戶端緩衝指的是所有接入到 Redis 伺服器 TCP 連接的輸入輸出緩衝。

輸入緩衝無法控制,最大空間為 1G,如果超過將斷開連接。而且輸入緩衝區不受 maxmemory 控制,假設一個 Redis 實例設置了 maxmemory 為 4G,已經存儲了 2G 數據,但是如果此時輸入緩衝區使用了 3G,就已經超出了 maxmemory 限制,可能導致數據丟失、鍵值淘汰或者 OOM。

輸入緩衝區過大主要是因為 Redis 的處理速度跟不上輸入緩衝區的輸入速度,並且每次進入輸入緩衝區的命令包含了大量的 bigkey。

輸出緩衝通過參數 client-output-buffer-limit 控制,其格式如下所示。

  1. client-output-buffer-limit [hard limit] [soft limit] [duration]

hard limit 是指一旦緩衝區大小達到了這個閾值,Redis 就會立刻關閉該連接。而 soft limit 和時間 duration 共同生效,比如說 soft time 為 64mb、duration 為 60,則只有當緩衝區持續 60s 大於 64mb 時,Redis 才會關閉該連接。

普通客戶端是除了複製和訂閱的客戶端之外的所有連接。Reids 對其的預設配置是 client-output-buffer-limit normal 0 0 0 , Redis 並沒有對普通客戶端的輸出緩衝區做限制,一般普通客戶端的記憶體消耗可以忽略不計,但是當有大量慢連接客戶端接入時這部分記憶體消耗就不能忽略,可以設置 maxclients 做限制。特別當使用大量數據輸出的命令且數據無法及時推送到客戶端時,如 monitor 命令,容易造成 Redis 伺服器記憶體突然飆升。相關案例可以查看這篇文章美團在Redis上踩過的一些坑-3.redis記憶體占用飆升

從客戶端用於主從複製,主節點會為每個從節點單獨建立一條連接用於命令複製,預設配置為 client-output-buffer-limit slave 256mb 64mb 60。當主從節點之間網路延遲較高或主節點掛載大量從節點時這部分記憶體消耗將占用很大一部分,建議主節點掛載的從節點不要多於 2 個,主從節點不要部署在較差的網路環境下,如異地跨機房環境,防止複製客戶端連接緩慢造成溢出。與主從複製相關的一共有兩類緩衝區,一個是從客戶端輸出緩衝區,另外一個是下麵會介紹到的複製積壓緩衝區。

訂閱客戶端用於發佈訂閱功能,連接客戶端使用單獨的輸出緩衝區,預設配置為 client-output-buffer-limit pubsub 32mb 8mb 60,當訂閱服務的消息生產快於消費速度時,輸出緩衝區會產生積壓造成記憶體空間溢出。

輸入輸出緩衝區在大流量場景中容易失控,造成 Redis 記憶體不穩定,需要重點監控。可以定期執行 client list 命令,監控每個客戶端的輸入輸出緩衝區大小和其他信息。

屬性名屬性說明
qbuf 查詢緩衝區的長度(位元組為單位, 0 表示沒有分配查詢緩衝區)
qbuf-free 查詢緩衝區剩餘空間的長度(位元組為單位, 0 表示沒有剩餘空間)
obl 輸出緩衝區的長度(位元組為單位, 0 表示沒有分配輸出緩衝區)
oll 輸出列表包含的對象數量(當輸出緩衝區沒有剩餘空間時,命令回覆會以字元串對象的形式被入隊到這個隊列里)
  1. 127.0.0.1:6379> client list
  2. id=3 addr=127.0.0.1:58161 fd=8 name= \
  3. age=1408 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 \
  4. qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 \
  5. events=r cmd=client

client list 命令執行速度慢,客戶端較多時頻繁執行存在阻塞redis的可能,所以一般可以先使用 info clients 命令獲取最大的客戶端緩衝區大小。

  1. 127.0.0.1:6379> info clients
  2. # Clients
  3. connected_clients:1
  4. client_recent_max_input_buffer:2
  5. client_recent_max_output_buffer:0
  6. blocked_clients:0

複製積壓緩衝區是Redis 在 2.8 版本後提供的一個可重用的固定大小緩衝區,用於實現部分複製功能。根據 repl-backlog-size 參數控制,預設 1MB。對於複製積壓緩衝區整個主節點只有一個,所有的從節點共用此緩衝區。因此可以設置較大的緩衝區空間,比如說 100MB,可以有效避免全量複製。有關複製積壓緩衝區的詳情可以看我的舊文章 Redis 複製過程詳解

AOF 重寫緩衝區:這部分空間用於在 Redis AOF 重寫期間保存最近的寫入命令。AOF 重寫緩衝區的大小用戶無法控制,取決於 AOF 重寫時間和寫入命令量,不過一般都很小。有關 AOF 持久化的詳情可以看我的舊文章 Redis AOF 持久化詳解

Redis 記憶體碎片

Redis 預設的記憶體分配器採用 jemalloc,可選的分配器還有:glibc、tcmalloc。記憶體分配器為了更好地管理和重覆利用記憶體,分配記憶體策略一般採用固定範圍的記憶體塊進行分配。具體的分配策略後續會具體講解,但是 Redis 正常碎片率一般在 1.03 左右(為什麼是這個值)。但是當存儲的數據長度長度差異較大時,以下場景容易出現高記憶體碎片問題:

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

這部分內容我們後續再詳細講解 jemalloc,因為大量的框架都會使用記憶體分配器,比如說 Netty 等。

子進程記憶體消耗

子進程記憶體消耗主要指執行 AOF 重寫 或者進行 RDB 保存時 Redis 創建的子進程記憶體消耗。Redis 執行 fork 操作產生的子進程記憶體占用量表現為與父進程相同,理論上需要一倍的物理記憶體來完成相應的操作。但是 Linux 具有寫時複製技術 (copy-on-write),父子進程會共用相同的物理記憶體頁,當父進程處理寫請求時會對需要修改的頁複製出一份副本完成寫操作,而子進程依然讀取 fork 時整個父進程的記憶體快照。

如上圖所示,fork 時只拷貝 page table,也就是頁表。只有等到某一頁發生修改時,才真正進行頁的複製。

但是 Linux Kernel 在 2.6.38 記憶體增加了 Transparent Huge Pages (THP) 機制,簡單理解,它就是讓頁大小變大,本來一頁為 4KB,開啟 THP 機制後,一頁大小為 2MB。它雖然可以加快 fork 速度( 要拷貝的頁的數量減少 ),但是會導致 copy-on-write 複製記憶體頁的單位從 4KB 增大為 2MB,如果父進程有大量寫命令,會加重記憶體拷貝量,都是修改一個頁的內容,但是頁單位變大了,從而造成過度記憶體消耗。例如,以下兩個執行 AOF 重寫時的記憶體消耗日誌:

  1. // 開啟 THP
  2. C * AOF rewrite: 1039 MB of memory used by copy-on-write
  3. // 關閉 THP
  4. C * AOF rewrite: 9MB of memory used by copy-on-write

這兩個日誌出自同一個 Redis 進程,used_memory 總量是 1.5GB,子進程執行期間每秒寫命令量都在 200 左右。當分別開啟和關閉 THP 時,子進程記憶體消耗有天壤之別。所以,在高併發寫的場景下開啟 THP,子進程記憶體消耗可能是父進程的數倍,造成機器物理記憶體溢出。

所以說,Redis 產生的子進程並不需要消耗 1 倍的父進程記憶體,實際消耗根據期間寫入命令量決定,所以需要預留一些記憶體防止溢出。並且建議關閉系統的 THP,防止 copy-on-write 期間記憶體過度消耗。不僅是 Redis,部署 MySQL 的機器一般也會關閉 THP。

本人博客,歡迎來玩

微信公眾號原文

參考文章


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • STM32之串口DMA接收不定長數據 引言 在使用stm32或者其他單片機的時候,會經常使用到串口通訊,那麼如何有效地接收數據呢?假如這段數據是不定長的有如何高效接收呢? 同學A:數據來了就會進入串口中斷,在中斷中讀取數據就行了! 中斷就是打斷程式正常運行,怎麼能保證高效呢?經常把主程式打斷,主程式 ...
  • 好用的軟體/終端 命令別名 ip切換 imgcat 終端查看圖片 autojump 目錄快捷跳轉 titan 密碼記錄工具 tldr man 的簡單版,線上查詢linux命令示例 ssh 別名/免密碼登錄 ps 行含義 htop awk parallels descktop Jump Desktop ...
  • 一、打開CMD,進入想安裝的目錄,輸入如下圖所示,安裝一個blog的項目: 二、進入指定目錄即可看到生成的blog項目,如下圖: ...
  • 我們使用 linux 文件系統擴展屬性,能夠對linux文件系統進行進一步保護;從而給文件 賦予一些額外的限制;在有些情況下,能夠對我們的系統提供保護; chattr命令用來改變文件屬性。這項指令可改變存放在ext2文件系統上的文件或目錄屬性,這些屬性共有以下8種模式:詳細作用,可以查看man手冊 ...
  • 導讀 第一章:初識Hadoop 第二章:更高效的WordCount 第三章:把別處的數據搞到Hadoop上 第四章:把Hadoop上的數據搞到別處去 第五章:快一點吧,我的SQL 第六章:一夫多妻制 第七章:越來越多的分析任務 第八章:我的數據要實時 第九章:我的數據要對外 第十章:牛逼高大上的機器 ...
  • 實現目標 搭建兩台MySQL伺服器(一主一從),一臺作為主伺服器,一臺作為從伺服器,主伺服器進行寫操作,從伺服器進行讀操作。 工作流程概述 主伺服器: 開啟二進位日誌 配置唯一的server id 獲得master二進位日誌文件名及位置 創建一個用於slave和master通信的用戶賬號 從伺服器: ...
  • 什麼是索引 + 索引是對 資料庫中一列或者多列的值進行排序的一中結構 ,使用索引可以快速訪問資料庫中表的特定信息。索引的一個主要的目的就是加快檢索表中數據,亦即能協助信息搜索者儘快的找到符合限制條件的記錄的輔助數據結構。 + 簡單來說索引就是資料庫的目錄。 索引有什麼作用 索引的最大作用就是加快數據 ...
  • 1. 前言 電腦的基本工作就是處理數據,包括磁碟文件中的數據,通過網路傳輸的數據流或數據包,資料庫中的結構化數據等。隨著互聯網、物聯網等技術得到越來越廣泛的應用,數據規模不斷增加,TB、PB量級成為常態,對數據的處理已無法由單台電腦完成,而只能由多台機器共同承擔計算任務。而在分散式環境中進行大數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...