redis入門指南(四)—— redis如何節省空間

Dylan~發表於2020-07-13

寫在前面

  學習《redis入門指南》筆記,結合實踐,只記錄重要,明確,屬於新知的相關內容。

 

節省空間

  1、redis對於它所支援的五種資料型別,每種都提供了兩種及以上的編碼方式去儲存(具體對應的編碼方式可以百度)。因為基於記憶體的緣故,所以為了平衡時間與空間的使用效率在元素數量較多或較少時採用不同的策略,當然對於使用者這是透明的。

  2、檢視redis鍵值的內部編碼方式

    OBJECT ENCODING key

  3、對於每一個鍵,都會有一個結構去儲存它的資料型別,編碼格式,資料地址等資訊。

1 typedef struct redisObject
2 {
3     unsigned type:4;            // 這種語法叫做位欄位
4     unsigned notused:2;
5     unsigned encoding:4;
6     unsigned lru:22;
7     int refcount;
8     void *ptr; 
9 }

   4、對於字串型別(示意結構如下),鍵值可以用一個64位整數表示時,就會使用資料指標表示資料內容(REDIS_ENCODING_INT編碼方式),以節省空間;redis3.0引入了REDIS_ENCODING_EMBSTR編碼方式,在鍵值長度小於39時,字串本身就會跟在redisObject結構之後,減少申請釋放記憶體次數;當對REDIS_ENCODING_EMBSTR編碼方式的字串做任何修改後,都會改為REDIS_ENCODING_RAW編碼方式;redis啟動後,會預先建立10000個從0~9999這些數字的redisObject結構,當我們set這些值時會直接引用向它們,並自增refcount這個引用,但如果在配置檔案引數中設定了maxmemory引數,將不會預先儲存這些共享物件。

1 struct shshdr          // 僅僅是示意結構
2 {
3     int len;
4     int free;
5     char buf[];
6 }

  5、對於雜湊型別,當欄位個數小於hash-max-ziplist-entries(配置檔案引數,預設512)並且每個欄位值長度小於hash-max-ziplist-value(配置檔案引數,預設64)時,採用REDIS_ENCODING_ZIPLIST(示意結構如下)編碼,否則採用REDIS_ENCODING_HT(真正的雜湊表);對於redis的鍵值對儲存也是用雜湊表,但並不使用redisObject結構,所以數字鍵名不會比字串名節省空間。

1 zlbytes // uint32 整個結構佔用的空間
2 zltail  // uint32 末尾元素偏移
3 zllen   // unit16 元素數量     ————————————————————
4 元素1 ----------------------> | 前一個元素大小     |
5 元素2                         | 當前元素的編碼型別 | 
6 元素3                         | 當前元素大小       |
7 ......                        | 當前元素內容      |
8 zlend   // 結尾標識,永遠為255  ————————————————————

  REDIS_ENCODING_ZIPLIST中的每個元素由四部分構成。

  第一部分儲存前一個元素大小,用於倒序查詢,當前一個元素小於254位元組時,第一部分佔用一個位元組,否則佔用5個位元組;

  第二、三部分為元素編碼型別和大小,當元素長度不大於63位元組時,編碼為ZIP_STR_06B(即0<<6),同時第三部分用6個二進位制位記錄長度,此時二、三部分佔用一個位元組;當元素長度大於63且小於16383位元組時,二、三部分佔用2位元組,大於16383時佔用5位元組;

  第四部分如果元素內容可以轉為數字的話會用相應數字儲存,並用二、三部分來表示元素數字的型別(int16_t,int32_t等)。

  使用REDIS_ENCODING_ZIPLIST儲存雜湊型別時,元素1儲存欄位1,元素2儲存欄位1值;插入,刪除,查詢(一跳一跳查詢,跳過欄位值)時都將移動後面的元素或遍歷,因此上文的兩個引數不能過大。

  6、對於列表型別,有REDIS_ENCODING_LINKEDLIST 和REDIS_ENCODING_ZIPLIST兩種編碼,也有list-max-ziplist-entries和list-max-ziplist-value兩個引數控制變換編碼的時機;REDIS_ENCODING_LINKEDLIST即雙向連結串列,優化方式與字串型別的鍵值相同;較新版本的redis增加了REDIS_ENCODING_QUICKLIST編碼方式,它將一個長列表分成若干個以連結串列形式組織的ziplist,在減少空間佔用的同時,提示REDIS_ENCODING_ZIPLIST編碼的效能。

  7、對於集合型別,有REDIS_ENCODING_HT和REDIS_ENCODING_INTSET(結構如下),當元素都為整數且元素個數小於set-max-intset-entries(配置檔案引數,預設512)時,採用REDIS_ENCODING_INTSET;intset預設的encoding是INTSET_ENC_INT16(即2位元組),當無法滿足時會升級為INTSET_ENC_INT32或INTSET_ENC_INT64,同時調整之前的元素;intset按序儲存,採用二分查詢,插入和刪除效率較低;當初先非數字元素時,編碼立刻變為REDIS_ENCODING_HT,此時即便將非數字元素刪除,編碼也不會迴轉。

1 typedef struct intset
2 {
3     uint32_t encoding;
4     uint32_t length;
5     int8_t contents[];
6 } intset;

  8、對於有序集合型別,有REDIS_ENCODING_SKIPLIST和REDIS_ENCODING_ZIPLIST,同樣有兩個引數zset-max-ziplist-entries和zset-max-ziplist-value去控制何時變換編碼為跳躍表REDIS_ENCODING_SKIPLIST;在這種編碼方式下使用兩種資料結構來儲存有序集合型別鍵值,雜湊表用來儲存元素值與元素分數的對映關係以實現O(1)時間複雜度的命令,跳躍表儲存元素分數及到元素的對映用以實現排序功能;這裡的跳躍表允許分數相同的元素存在,並且增加了指向前一個元素的指標以實現倒序查詢;此時元素值是用redisObject結構儲存的,與字串型別鍵值優化方式相同,分數按照double儲存;採用REDIS_ENCODING_ZIPLIST時,按照“元素1的值,元素1的分數”的順序排列,並且分數是有序的。

 

 

相關文章