Redis學習-記憶體優化

来源:http://www.cnblogs.com/susufufu/archive/2017/11/21/7875210.html
-Advertisement-
Play Games

以下為個人學習Redis的備忘錄--記憶體優化,基於Redis4.0.2 1.隨時查看info memory,瞭解記憶體使用狀況:127.0.0.1:6379> info memory# Memoryused_memory:2314624 //(位元組單位形式)used_memory_human:2.21 ...


以下為個人學習Redis的備忘錄--記憶體優化,基於Redis4.0.2

1.隨時查看info memory,瞭解記憶體使用狀況
127.0.0.1:6379> info memory
# Memory
used_memory:2314624 //(位元組單位形式)
used_memory_human:2.21M //Redis已分配的記憶體總量(易讀單位形式)
used_memory_rss:1282048
used_memory_rss_human:1.22M //操作系統為Redis進程分配的記憶體總量
used_memory_peak:18010560
used_memory_peak_human:17.18M //最大使用記憶體總量(峰值)
used_memory_peak_perc:12.85% 
used_memory_overhead:2078792
used_memory_startup:963088
used_memory_dataset:235832
used_memory_dataset_perc:17.45%
total_system_memory:4294967296
total_system_memory_human:4.00G
used_memory_lua:37888
used_memory_lua_human:37.00K //緩存Lua腳本占用的記憶體
maxmemory:0
maxmemory_human:0B //最大記憶體限制,0表示無限制
maxmemory_policy:noeviction //超過記憶體限制後的處理策略
mem_fragmentation_ratio:0.55 //碎片率(used_memory_rss/used_memory的比值),>1表示有碎片,<1表示部分Redis的記憶體被系統交換到硬碟(此時Redis性能變差)
mem_allocator:libc 
active_defrag_running:0
lazyfree_pending_objects:0

2.Redis主進程的記憶體消耗:
  • Redis自身使用的記憶體:消耗很少,3MB多點
  • 對象記憶體
  • 緩衝記憶體
  • 記憶體碎片
2.1對象記憶體:所有key對象長度 + 所有value對象長度
  • 每次創建鍵值對時,至少創建兩個類型對象:key對象、value對象,應該使用短鍵名
2.2緩衝記憶體
  • 每個客戶端的輸入、輸出緩衝記憶體:
    • 輸入緩衝最大1G,超出則關閉該客戶端連接;
    • 輸出緩衝:16KB的固定緩衝區、動態緩衝區,動態緩衝區可通過client-output-buffer-limit配置參數限制(根據客戶端類型normal、slave、pubsub,分開設置)
      • client-output-buffer-limit normal 0 0 0
      • client-output-buffer-limit slave 256mb 64mb 60 //超過256MB時,或者持續超過64MB達60秒,關閉連接
      • client-output-buffer-limit pubsub 32mb 8mb 60
  • 複製積壓緩衝記憶體:用於主從複製的部分複製,所有客戶端共用該緩衝區,預設1MB,可通過repl-backlog-size調整,適當調大,可有效避免全量複製;
  • AOF緩衝記憶體:用於保存在AOF重寫期間的寫命令,便於重寫完畢後把緩衝的命令追加到AOF文件中;
2.3記憶體碎片
  • 當存儲的數據長短差異較大時,就容易出現大量記憶體碎片,應該儘可能地保持數據對齊或使用固定長度的字元串;
  • 記憶體碎片只能通過完全重啟Redis來清除;
3.Redis子進程記憶體消耗
  • 在執行AOF重寫和RDB快照持久化時,會fork一個子進程,父子進程將共用此刻的記憶體快照,期間,在Linux下使用寫時複製技術:父進程會為新進的寫命令請求需要修改的記憶體頁複製出一份副本來完成寫操作,子進程結束後,父進程再把該副本覆蓋回原來的記憶體頁。
  • Linux預設開啟的THP把寫時複製期間的記憶體頁複製單位從4KB變為2MB,加大了持久化時的記憶體消耗,應該關閉該功能:sudo echo never > /sys/kernel/mm/transparent_hugepage/enabled
4.記憶體管理
  • 設置記憶體上限,並指定記憶體回收策略;
  • maxmemory配置參數可限制當前Redis實例可使用的最大記憶體;
  • 通過config set maxmemory可根據業務需求,動態調整記憶體限制;
  • 通過設置記憶體上限,可方便地在一臺伺服器上部署多個Redis實例
4.1記憶體回收策略:
  • 為鍵設置過期屬性,Redis採用惰性刪除和定時任務刪除機制實現過期鍵的記憶體回收;
    • 惰性刪除:在讀取鍵時才檢查是否過期
    • 定時任務刪除:通過hz配置參數設置頻率,預設每秒10次;
  • 記憶體溢出控制策略:共6中策略,通過maxmemory-policy配置參數控制,預設noeviction(不刪除,拒絕寫入,返回錯誤)
    • LRU演算法表示最近最少使用的,LFU演算法表示最不常用的:
      • #volatile-lru - >在設置了過期的key中,刪除最近最少使用的key,直到空間足夠為止
      • #allkeys-lru - >從所有key里刪除最近最少使用的key,不管有沒設置過期,直到空間足夠為止
      • #volatile-lfu - >在設置了過期的key中,刪除最少使用的key,直到空間足夠為止
      • #allkeys-lfu - >從所有key里刪除最少使用的key,不管有沒設置過期,直到空間足夠為止
      • #volatile-random - >刪除一個過期集合中的隨機key。
      • #allkeys-random - >刪除一個隨機key,不管有沒設置過期。
      • #volatile-ttl - >刪除即將過期的key(次TTL)
      • #noviction - >不刪除,拒絕寫入,寫入操作時返回錯誤。
  • maxmemory-samples 5 是說每次進行淘汰的時候,會隨機抽取5個key 從裡面淘汰最少使用的(預設選項)
  • 應避免記憶體溢出,因為在記憶體溢出且非noeviction策略時,會頻繁觸發回收記憶體的操作,影響Redis性能,若有從節點,還會把刪除命令同步給從節點;
  • 對於只做緩存的場景下,可通過調小maxmemory,並執行一次命令,如果使用非noeviction策略,則會一次性回收到maxmemory指定的記憶體使用量,實現記憶體的快速回收,但會導致數據丟失和短暫阻塞;
5.記憶體優化:
  • Redis存儲的所有數據都使用redisObject來封裝,包括string、hash、list、set、zset
  • redisObject的欄位:
    • type欄位:保存對象使用的數據類型,命令type {key}返回值對象的數據類型
    • encoding欄位:保存對象使用的內部編碼類型,命令object encoding {key}返回值對象的內部編碼類型
    • lru欄位:記錄對象最後一次被訪問的時間(用於記憶體回收),命令object idletime {key}查看鍵的空閑時間(可配合scan命令批量查找長期空閑的鍵進行清理)
    • refcount欄位:記錄對象的引用計數(用於回收),命令object refcount {key}查看鍵的引用數
    • *ptr欄位:存儲值對象的數據或指針,如果是整數,則直接存儲數據,否則表示指向數據的指針
  • 字元串長度在39位元組以內對象,在創建redisObject封裝對象時只需分配記憶體1次,可提高性能;
  • 縮減鍵、值對象的長度:簡化鍵名,使用高效的序列化工具來序列化值對象,還可使用壓縮工具(Google Snappy)壓縮序列化後的數據;
  • 共用對象池:Redis內部維護[0-9999]的整數對象池,對於0-9999的內部整數類型的元素、整數值對象都會直接引用整數對象池中的對象,因此儘量使用整數對象可節省記憶體;
    • 註意:
      • 啟用LRU相關的溢出策略時,無法使用共用對象池;
      • 對於ziplist編碼的值對象,也無法使用共用對象池(成本過高);
  • Redis對字元串的優化
    • Redis所有key都是string類型,且value對象的數據除了整數之外,最終也都使用string來存儲;
    • Redis字元串結構採用SDS(內部簡單動態字元串):
      • int len欄位:已用位元組長度
      • int free欄位:未用位元組長度
      • char buf[]欄位:位元組數組
    • SDS字元串特點:
      • 獲取字元串長度、未用長度速度快,時間複雜度為O(1)
      • 用位元組數組保存數據,支持安全的二進位數據存儲
      • 內部實現了預分配記憶體機制,降低記憶體分配次數
      • 惰性刪除機制,字元串縮減後的空間不釋放,作為預分配空間保留
    • SDS字元串記憶體預分配機制:
      • 首次創建時,不做預分配,數據剛好填滿位元組數組,len欄位為位元組數組長度,free欄位為0
      • 在修改字元後,如果原本的free空間不足,且當前總數據大小<1MB,則每次預分配1倍容量,而如果總數據大小>1MB,則每次預分配1MB容量。
      • 如:(忽略len、free欄位所占記憶體,只考慮buf所占記憶體)
        • 對於首次創建的30位元組字元串,對它執行append追加10位元組,將使用(30+10)+40+1=81位元組的記憶體
        • 而直接set這40位元組的字元串,只使用41位元組的記憶體(1位元組為結尾標識'\0')
    • 應該儘量避免頻繁執行增長字元串的命令,如append、setrange,改為直接用set一次性創建字元串,減少預分配帶來的記憶體浪費和降低記憶體碎片率;
    • 字元串重構:編碼為ziplist的hash數據結構的妙用1
      • 對於非簡單字元串數據,可用hash數據結構代替
      • 因為小hash使用ziplist編碼,可節省記憶體(字元串數據必須小於hash-max-ziplit-value配置的值)
      • 且hash可用使用hmget、hmset命令,支持field-value的部分讀取修改,而不必每次都整體存取 
6.合理設置內部編碼配置參數:
Redis的每種數據結構都有至少兩種內部數據編碼類型:object encoding {key} 獲取key對應的value對象的編碼類型
string int 8個位元組的長整型
embstr <=39位元組的字元串
raw >39位元組的字元串(最大不能超過512MB)
hash ziplist 壓縮列表(模擬雙向鏈表),記憶體占用少,但讀寫時間複雜度為O(n²)
hashtable 哈希表,記憶體占用較大,但讀寫時間複雜度為O(1)
list quicklist (ziplist) 快速雙向鏈表(每個節點都是ziplist)
set intset 整數集合
hashtable 哈希表
zset ziplist 壓縮列表
skiplist 跳躍表
  • Redis在寫入數據時自動完成編碼轉換,且在超過配置的限制值時將轉換為新的內部編碼,動態修改限制參數不會回退為舊編碼,只有在重啟Redis重新載入數據後才會回退;
  • ziplist編碼:
    • ziplist內部結構:
      • zlbytes欄位:int-32類型,記錄整個ziplist總位元組數,便於重新調整ziplist空間;
      • zltail欄位:記錄距離尾節點的偏移量,便於尾節點的彈出操作;
      • zllen欄位:記錄節點數量;
      • entry1...entryN節點:記錄具體的節點,長度根據具體的數據
        • prev_entry_bytes_length:記錄前一節點所占空間,用於快速定位前一節點實現列表的反向迭代;
        • encoding:當前節點編碼和長度,前兩位表示編碼類型(字元串、整數),其餘位表示數據長度;
        • contents:保存節點的值,針對實際數據長度做記憶體占用優化;
      • zlend欄位:記錄列表結尾,占1個位元組
    • ziplist是一塊連續的記憶體,它模擬了雙向鏈表的功能,兩端的push和pop速度快,但是對中間元素的修改不方便,每次在中間插入、刪除都會引發記憶體重新分配和數據拷貝,ziplist越長性能越低,所以ziplist僅適合存儲小對象和長度有限的數據。
    • 因此,ziplist的長度不宜過長(建議1000個以內),且元素大小不宜過大(建議512位元組以內),且最好每個元素的大小差別不宜過大(否則碎片多)。
    • 對於較小的hash、zset 數據結構,Redis會自動使用ziplist編碼,雖然list的編碼為quicklist,但list的節點也是ziplist編碼。
    • hash同時滿足以下條件則使用ziplist編碼,超過則使用hashtable編碼
      • hash-max-ziplist-entries 64 
      • hash-max-ziplist-value 512 
    • list使用的是quicklist編碼,quicklist的每個節點都是ziplist,以下指定節點的設置
      • list-max-ziplist-size -2 //>0時表示每個節點最多包含幾個數據項,即ziplist的長度。<0時,只能取-5~-1,指每個節點ziplist的最大位元組大小≤64KB~4KB位元組(超過該限制時,則新建一個節點)
      • list-compress-depth n //n表示兩端不被壓縮的節點個數(壓縮所有中間節點),預設0不壓縮
    • zset同時滿足以下條件則使用ziplist編碼,超過則使用skiplist編碼
      • zset-max-ziplist-entries 128
      • zset-max-ziplist-value 64
    • set所有元素都為整數,且個數小於以下參數時,使用intset編碼,否則使用hashtable編碼
      • set-max-intset-entries 512
  • intset編碼:
    • intset編碼是無序集合(set)類型編碼的一種,內部表現為存儲有序、不重覆的整數集合
    • intset結構:
      • encoding:根據集合內最長整數值確定所有元素的類型(int-16、int-32、int-64),當插入一個更長的整數類型時,會觸發類型升級操作(會導致重新申請記憶體空間,並複製數據到新數組)
      • length:集合元素的個數;
      • contents:整數數組,按從小到大順序保存;
    • 所以,在使用set集合,且為整數時,應該保持整數長度類型的一致性,避免記憶體浪費;
    • set小集合重構(編碼為ziplist的hash數據結構的妙用2):因為當set集合中有一個是非整數時,將使用hashtable編碼,無法使用intset實現記憶體優化,如果集合元素個數和大小滿足hash的ziplist編碼條件,則此時可用hash類型來模擬集合,把hash的field設為set的元素,而hash的value設為1位元組占位符即可;
7.使用32位Redis實例:
  • 假如緩存數據小於4GB,就使用32位Redis實例,因為對於每一個key,將使用更少的記憶體,指針占用的位元組數更少。
  • 使用make 32bit命令編譯生成32位的redis。但記憶體受限在4G內,不過他們的RDB和AOF文件是相容在32位和64位的。
8.儘可能的使用hash數據結構
  • 因為Redis在儲存小於100個欄位的Hash結構上,其存儲效率是非常高的。所以在不需要集合(set)操作或list的push/pop操作的時候,儘可能的使用hash結構
  • 使用單命令多參數的命令取代多命令單參數的命令:
    • set -> mset
    • get -> mget
    • lset -> lpush, rpush
    • lindex -> lrange
    • hset -> hmset
    • hget -> hmget 
9.減少key的數量(編碼為ziplist的hash數據結構的妙用3)
  • 把大量value為string的普通key-value抽象為分組的小hash的field-value,建議field總個數<1000,value的長度<512位元組,value越小,越省空間(最好50位元組以內)
key = username0000 value =strs  ... key = username9999 value =strs 
  • 以上可重構為10組hash key,每組1000個field
key = username0 field = 000 value = str ... field =999 value =str  ... key = username9 field = 000 value = str ... field =999 value =str 
  • 對於只含可計算的field的Hash:
    • 也可使用分組hash:如下,每100個用戶ID共用一個hash key
      • key=userId/100, field1=userId%100, field1Value=str, field2=userId%100, field2Value=str, ...
      • 即:userId為1~100的所有用戶的userId-value鍵值對都存儲在key=0的field-value中,而101~200則存在key=1中,......

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

-Advertisement-
Play Games
更多相關文章
  • 判斷pdf、word文檔、圖片等文件類型(格式)、大小的簡便方法 ...
  • cookie(儲存在用戶本地終端上的數據) Cookie,有時也用其複數形式 Cookies,指某些網站為了辨別用戶身份、進行 session 跟蹤而儲存在用戶本地終端上的數據(通常經過加密)。 下載與引入jquery.cookie.js: 由於jquery.cookie.js是基於jquery的, ...
  • 在學習廖雪峰前輩的JavaScript教程中,遇到了一些需要註意的點,因此作為學習筆記列出來,提醒自己註意! 如果大家有需要,歡迎訪問前輩的博客https://www.liaoxuefeng.com/學習。 條件判斷 JavaScript使用if(){......}else{......}來進行條件 ...
  • 好久沒用node了,想重新拾起來發現node還有相關模塊的版本都太低了,使用npm install全是報版本低的警告。 這裡記錄一下,版本管理和node_modules更新的方法。 我用的是Windows的系統,Windows下有一個node版本管理工具叫:gnvm。Linux下是nvm。 gnvm ...
  • 一,效果圖。 二,工程圖。 三,代碼。 ViewController.h ViewController.m ...
  • IJKPlayer拖動播放進度會導致重新請求數據,未使用已經緩衝好的數據,所以應該儘量控制緩衝區大小,減少不必要的數據損失。 mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 100 * 102 ...
  • 1、一個文件的基本框架為:文件名、文件地址、文件大小、文件最大的大小、文件的增量(Filegrowth)。 2、文件有mdf、ndf、ldf 三種文件的區別。 3、文件組可以進行文件的管理 FileGroup 預設的文件組是 Primary 日誌文件無文件組的概念,存在Log文件中。 4、 alte ...
  • 1.展示當前資料庫 所有表名(前提必須進入資料庫,進入資料庫語句:【use 資料庫名;】)mysql> show create table 表名; 2.看mysql支持哪些存儲引擎:mysql> show engines; 3.查看mysql當前預設的存儲引擎:mysql> show variabl ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...