最近復習到redis底層編碼的時候突然想到這個問題,為什么hash比string做緩存更節省內存與效率更高?一時間沒想明白,百度一大堆一個關鍵點都沒有答上的,全是介紹什么ziplist、sds編碼就沒了,至於問題關鍵在哪也沒說明白,最煩這種人了,把博客生態都搞得亂七八糟的,這里我把自己的想法分享給大家,不對的話請指教。 ps:我都是寫有道雲筆記的。
首先看到我的答案前提下需要先了解hash的ziplist跟string的sds編碼,這個我就不說了,還是很好查到資料的
我也簡單說明一點吧:
string底層采用sds編碼(在不是數據的情況下),在redis3.2版本后會根據string的大小來采用不同的位數的sds編碼,下面是不同的sds中c語言源碼跟sdshdr8格式結構圖:
1 typedef char *sds; 2 struct __attribute__ ((__packed__)) sdshdr5 { 3 unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ 4 char buf[]; 5 }; 6 struct __attribute__ ((__packed__)) sdshdr8 { 7 uint8_t len; //已用 8 uint8_t alloc; //buf[]總共分配的長度 9 unsigned char flags; //用於內存對齊放在前面的標識位,char類型占一個字節(8位) 10 char buf[]; 11 }; 12 struct __attribute__ ((__packed__)) sdshdr16 { 13 uint16_t len; /* used */ 14 uint16_t alloc; /* excluding the header and null terminator */ 15 unsigned char flags; /* 3 lsb of type, 5 unused bits */ 16 char buf[]; 17 }; 18 struct __attribute__ ((__packed__)) sdshdr32 { 19 uint32_t len; /* used */ 20 uint32_t alloc; /* excluding the header and null terminator */ 21 unsigned char flags; /* 3 lsb of type, 5 unused bits */ 22 char buf[]; 23 }; 24 struct __attribute__ ((__packed__)) sdshdr64 { 25 ........
下面是ziplist的結構圖:
- zlbytes:32bit,表示ziplist占用的字節總數。
- zltail:32bit,表示ziplist表中最后一項(entry)在ziplist中的偏移字節數。通過zltail我們可以很方便地找到最后一項,從而可以在ziplist尾端快速地執行push或pop操作,保證了時間復雜度為O(1)
- zlen:16bit, 表示ziplist中數據項(entry)的個數。
- entry:表示真正存放數據的數據項,長度不定
- zlend: ziplist最后1個字節,是一個結束標記,值固定等於255。
答案:
首先前提是在hash使用ziplist編碼的情況。首先我們存入的是多個緩存,每個sds都需要包含len(已用長度)、alloc(buf[]分配長度)、flags(標識),bug[],而在ziplist中只需要前面幾個prerawlen(前一個元素的字節長度)、len(entry中數據的長度)、接着就是緊湊的數據了,這樣在多個字符串存儲的時候ziplist就省去了大量的數據外的空間占用,這就是省內存的關鍵。那為什么效率為何更高?正是因為空間占用的少,所以尋址的次數就會更少,效率也就更高