成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Redis 內存壓縮實戰,學習了!

數據庫 其他數據庫 Redis
在討論Redis內存壓縮的時候,我們需要了解一下幾個Redis的相關知識。

[[410334]]

 在討論Redis內存壓縮的時候,我們需要了解一下幾個Redis的相關知識。

壓縮列表 ziplist

Redis的ziplist是用一段連續的內存來存儲列表數據的一個數據結構,它的結構示例如下圖 

壓縮列表組成示例--截圖來自《Redis設計與實現》

  1.   zlbytes: 記錄整個壓縮列表使用的內存大小
  2.   zltail: 記錄壓縮列表表尾距離起始位置有多少字節
  3.   zllen: 記錄壓縮列表節點數量,值得注意的一點是,因為它只占了2個字節,所以最大值只能到65535,這意味著壓縮列表長度大于65535的時候,就只能通過遍歷整個列表來計算長度了
  4.   zleng: 壓縮列表末端標志位,固定值為OxFF
  5.   entry1-N: 壓縮列表節點, 具體結構如下圖

壓縮列表節點組成示例--截圖來自《Redis設計與實現》

其中

  1.   previous_entry_length: 上一個節點的長度
  2.   encoding: content的編碼以及長度
  3.   content: 節點數據

當我們查找一個節點的時候,主要進行一下操作:

  1.   根據zltail獲取最后一個節點的位置
  2.   判斷當前節點是否是目標節點
  3.   如果是,則返回數據
  4.   如果不是,則根據previous_entry_length計算上一個節點的起始位置,然后重新進行步驟2判斷

通過上述的描述,我們可以知道,ziplist每次數據更新的復雜度大約是O(N),因為它需要對N個節點進行內存重分配,查找一個數據的時候,復雜度是O(N),最壞情況下需要遍歷整個列表。

什么情況下會使用到ziplist呢?

Redis會使用到ziplist的數據結構是Hash與List。

Hash結構使用ziplist作為底層存儲的兩個條件是:

  1.   所有的鍵與值的字符串長度都小于64字節的時候
  2.   鍵與值對數據小于512個

只要上述條件任何一個不滿足,Redis就會自動將這個Hash對象從ziplist轉換成hashtable。但這兩個閾值可以通過修改配置文件中的hash-max-ziplist-value與hash-max-ziplist-entries來變更。

List結構使用ziplist的條件與Hash結構一樣,當條件不滿足的時候,會從ziplist轉換成linkedlist,同樣我們可以修改list-max-ziplist-value與hash-max-ziplist-entries來使用不同的閾值。

為什么Hash與List會使用ziplist來存儲數據呢?

因為

  1.   ziplist會比hashtable與ziplist節省跟多的內存
  2.   內存中以連續塊方式保存的數據比起hashtable與linkedlist使用的鏈表可以更快的載入緩存中
  3.   當ziplist的長度比較小的時候,從ziplist讀寫數據的效率比hashtable或者linkedlist的差異并不大。

本質上,使用ziplist就是以時間換空間的一種優化,但是他的時間損壞小到幾乎可以忽略不計,但卻能帶來可觀的內存減少,所以滿足條件時,Redis會使用ziplist作為Hash與List的存儲結構。

實戰

我們先拋出問題,在廣告程序化交易的過程中,我們經常需要為一個廣告投放計劃定制人群包,其存儲的形式如下: 

  1. 人群包ID => [設備ID_1, 設備ID_2 ... 設備ID_N] 

其中,人群包ID是Long型整數,設備ID是經過MD5處理,長度為32。在業務場景中,我們需要判斷一個設備ID是否在一個人群包中,來決定是否投放廣告。另外,Redis 系列面試題和答案全部整理好了,微信搜索Java技術棧,在后臺發送:面試,可以在線閱讀。

在傳統的使用Redis的場景, 我們可以使用標準的KV結構來存儲定向包數據,則存儲方式如下: 

  1. {人群包ID}_{設備ID_1} => true  
  2. {人群包ID}_{設備ID_2} => true 

如果我們想使用ziplist來繼續內存壓縮的話,我們必須保證Hash對象的長度小于512,并且鍵值的長度小于64字節。我們可以將KV結構的數據,存儲到預先分配好的bucket中。

我們先預估下,整個Redis集群預計容納的數據條數為10億,那么Bucket的數量的計算公式如下: 

  1. bucket_count = 10億 / 512 = 195W 

那么我們大概需要200W個Bucket(預估Bucket數量需要多預估一點,以防觸發臨界值問題) 我們先以下公式計算BucketID: 

  1. bucket_id = CRC32(人群包ID + "_" + 設備ID) % 200W 

那么數據在Redis的存儲結構就變成 

  1. bucket_id => {  
  2.    {人群包ID}_{設備ID_1} => true  
  3.    {人群包ID}_{設備ID_2} => true  

這樣我們保證每個bucket中的數據項都小于512,并且長度均小于64字節。點擊這里可以刷 Redis 系列面試題。

我們以2000W數據進行測試,前后兩者的內存使用情況如下:

數據集大小 存儲模式 Bucket數量 所用內存 碎片率 Redis占用的內存
2000W 壓縮列表 200W 928M 1.38 1.25G
2000W 壓縮列表 5W 785M 1.48 1.14G
2000W 直接存儲 - 1.44G 1.03 1.48G

在這里需要額外引入一個概念 – 內存碎片率。

  1. 內存碎片率 = 操作系統給Redis分配的內存 / Redis存儲對象占用的內存 

因為壓縮列表在更新節點的時候,經常需要進行內存重分配,所以導致比較高的內存碎片率。我們在做技術方案比較的時候,內存碎片率也是非常需要關注的指標之一。

但有很多手段可以減少內存碎片率,比如內存對其,甚至更極端的直接重做整個Redis內存(利用快照或者從節點來重做內存)都能有效的減低內存碎片率。

我們在本次實驗中,因為存儲的數值比較大(單個KEY約34個字節),所以實際節省內存不是很多,但依然能節約35%-50%的內存使用。

在實際的生產環境中,我們根據應用場景合理的設計壓縮存儲結構,部分業務甚至能達到節約70%的內存使用的效果。

壓縮列表能節省多少內存?

我們現在知道壓縮列表是通過將節點緊湊的排列在內存中,從而節省掉內存的。但他究竟節省了哪些內存從而能達到驚人的壓縮率呢?

首先為了明白這個細節,我們需要知道普通Key-Value結構在Redis中是如何存儲的。 

  1. typedef struct redisObject {  
  2.     unsigned type:4;        // 對象的類型  
  3.     unsigned encoding:4;    // 對象的編碼  
  4.     unsigned lru:LRU_BITS;  // LRU類型  
  5.     int refcount;           // 引用計數  
  6.     void *ptr;              // 指向底層數據結構的指針  
  7. } robj; 

Redis所有的對象都是通過上述結構來存儲, 假設我存儲Hello=>World這樣一個健值對到Redis中,除了存儲本身鍵值的數據外,還需要額外的24個字節來存儲redisObject對象。

而Redis存儲字符串使用的SDS數據結構 

  1. struct sdshdr8 {  
  2.     uint8_t len;        // 所保存字符串的長度  
  3.     uint8_t alloc;      // 分配的內存數量  
  4.     unsigned char flags;// 標志位,用于判斷sdshdr類型  
  5.     char buf[];         // 字節數組,用戶保存字符串  
  6. }; 

假如字符串的長度無法用unsigned int8來表示的話,Redis會使用能表達更大長度的sdshdr16結構來存儲字符串。

并且,為了減少修改字符串帶來的內存重分類問題,Redis會進行內存預分配,所以可能你僅僅為了保存五個字符,但Redis會為你預分配10 bytes的內存。

這意味著當我們存儲Hello這個字符串的時候,你需要額外的3個以上的字節。

Oh~~~,我只想保存Hello=>World這十個字符的數據,竟然需要的30~40個字節的數據來存儲額外的信息,比存儲數據本身的大小還多一些。這還沒包括Redis維護字典表所需要的額外的內存空間。

那么假設我們用ziplist來存儲這個數據,我們僅僅需要額外的2個字節用于存儲previous_entry_length與encoding。具體的計算方式可以參考Redis源碼或者《Redis設計與實現》第一部分第7章壓縮列表。

總結

從以上對比,我們可以看出,在存儲越小的數據的時候,使用ziplist來進行數據壓縮能得到更好的壓縮率。但副作用也很明顯,ziplist的更新效率遠遠低于普通K-V模式,并且會造成額外的內存碎片率。

在Redis中存儲大量數據的實踐過程中,我們經常會做一些小技巧來盡可能壓榨Redis的存儲能力。另外,關注公眾號Java技術棧,在后臺回復:面試,可以獲取我整理的 Redis 系列面試題和答案,非常齊全。 

 

責任編輯:龐桂玉 來源: Java技術棧
相關推薦

2019-09-27 09:13:55

Redis內存機制

2020-09-01 07:47:32

Redis秒殺微信

2024-07-30 08:38:13

2012-02-15 10:40:37

JavaJava Socket

2020-03-05 09:51:20

內存分頁映射

2019-11-05 11:17:11

Java虛擬機技術Java 堆

2018-05-23 08:52:40

HBase內存壓縮算法

2018-05-07 09:48:49

AccordionHBase內存

2014-04-10 15:42:08

Linux系統內存

2022-12-07 18:45:22

內存數據庫Redis

2021-02-22 07:58:52

Linux壓縮解壓

2012-05-03 11:51:59

ApacheCXFJava

2023-10-23 11:22:06

Redis數據持久化

2009-12-25 17:55:05

Linux tar

2024-01-08 08:42:43

2024-11-11 10:00:00

2011-05-24 14:48:46

壓縮數據庫

2017-06-07 14:58:39

Redis源碼學習Redis事務

2022-07-05 08:41:03

Redis保存大數據

2021-11-30 06:32:19

Redis宕機集群
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 香蕉视频91| 日本不卡一区 | 日本福利视频 | 国产精品a级 | 一级日韩 | 啪啪免费网站 | 国产一区二区三区四区区 | 国产69久久精品成人看动漫 | 久久婷婷麻豆国产91天堂 | 日本在线中文 | 亚洲精品一二三 | 91免费版在线观看 | 亚洲福利在线视频 | 欧美福利 | 日本午夜视频 | 91精品国产综合久久久久久蜜臀 | 男女羞羞的网站 | 色又黄又爽网站www久久 | 亚洲高清在线 | 久久久久久黄 | 国产剧情一区 | 成人欧美一区二区三区在线观看 | 国产精品久久久久久久久久久久久 | 黄色a三级| 日韩视频免费在线 | 国产精品欧美日韩 | 日日夜夜天天干 | 欧美一区二区三区在线播放 | 成人免费视频网址 | 91精品国产91久久久久久三级 | 九九热九九 | 91久久久久久久久久久久久 | 亚洲精品不卡 | 久久久久久久久久久久久久久久久久久久 | 国产精品污www一区二区三区 | 久热精品在线播放 | 国产精品不卡一区二区三区 | 一区二区视频 | 四虎国产 | 国产二区三区 | 成人欧美 |