redis中为什么hash比string做缓存更节省内存与效率更高?

最近复习到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就省去了大量的数据外的空间占用,这就是省内存的关键。那为什么效率为何更高?正是因为空间占用的少,所以寻址的次数就会更少,效率也就更高

posted @ 2021-08-05 23:33  像灭霸一样看日出  阅读(1697)  评论(0编辑  收藏  举报