上一篇我们说到了内存篇,但是关于内存还有一些东西要了解,就是关于Redis数据类型的内部编码,这一篇来分析下

Redis对象类型的内部编码

redis支持的5种数据结构类型(字符串、哈希、列表、集合、有序集合),每种都至少支持两种内部编码,这样做的优势是,接口和底层编码实现的解耦合,当需要根据不同场景切换内部编码的时候,用户不受影响

关于Redis内部编码的转换,都符合以下规律:编码转换在Redis写入数据时完成,且转换过程不可逆,只能从小内存编码向大内存编码转换

接下来一一介绍~

字符串

字符串是最基础的类型,因为所有的键都是字符串类型,且字符串之外的其他几种复杂类型的元素也是字符串。字符串长度不能超过512MB。

内部编码:

int:8个字节的长整型。字符串值是整型时,这个值在C中使用long整型表示。

embstr:<=44字节的字符串。(可能会因版本不同而不同)

raw:大于44个字节的字符串

embstr和str都是用RedisObject和SDS保存数据,embstr使用的时候只分配一次内存空间(而redisObject和SDS是连续的),而raw需要分配两次内存空间(分别为RedisObject和SDS分配空间)。

与raw相比,embstr的好处在于创建时少分配一次空间,删除时少释放一次空间。对于embstr来说,如果字符串的长度增加需要重新分配内存时,整个redisObject和sds都需要重新分配空间

编码转换测试:

127.0.0.1:6379> set key1 66
  OK
  127.0.0.1:6379> object encoding key1
  "int"
  127.0.0.1:6379> set key3 qwertyuiolkjhgfdsazxcvbnmlkjhgfdsa
  OK
  127.0.0.1:6379> strlen key3
  (integer) 34
  127.0.0.1:6379> object encoding key3
  "embstr"
  127.0.0.1:6379> set key3 qwertyuioplkjhgfdsazxcvbnm,lkjhgfdsaqwertyuiolkjhgfdsazxcvbnmlkjhgfdsa
  OK
  127.0.0.1:6379> strlen key3
  (integer) 70
  127.0.0.1:6379> object encoding key3
  "raw "

列表

列表(list)用来存储多个有序的字符串,每个字符串称为元素,一个列表可以存储2^32-1个元素。Redis中的列表支持两端插入和弹出,并可以获得指定位置(或范围)的元素,可以充当数组、队列、栈等。

3.0的redis列表的内部编码可以是压缩列表(ziplist)或双端链表(linkedlist),而4.0的列表则只有quicklist快链表结构了。

在Redis早期版本,list底层使用的是ziplist和linkedlist(元素少的时候用ziplist,元素多的时候用linkedlist),后来因为链表指针的空间占用大换成了现在的quicklist,quicklist可以说是两者的混合体,它将linkedlist按段拆分,每一段是一个ziplist,每个ziplist用双向指针连接起来。ziplist默认存储8字节,可以通过配置调整

双端链表:由一个list结构和多个listNode结构组成

redis get编码 redis编码字符集_Redis

双端链表保存了头节点head和尾节点tail,并且每一个节点都有指向前和后的指针,还有链表长度len,dup、free和match为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。而链表中每个节点指向的是type为字符串的redisObject

编码转换测试:

127.0.0.1:6379> rpush list2 va1
  (integer) 1
  127.0.0.1:6379> object encoding list2
  "quicklist"

哈希

哈希也是常用的数据结构之一,是一种key-value形式的数据结构(注意区分哈希数据结构和key-value型数据库redis)。

数据结构哈希使用的是内部编码可以是压缩列表(ziplist)和哈希表(hashtable)两种。压缩列表ziplist在上面介绍过,与哈希表相比,压缩列表用于元素个数少、元素长度小的场景;其优势在于集中存储,节省空间

哈希表hashtable:一个hashtable由1个dict结构、2个dictht结构、1个dictEntry指针数组(称为bucket)和多个dictEntry结构组成。哈希表如下所示,不详解

redis get编码 redis编码字符集_redis get编码_02

只有同时满足下面两个条件时,才会使用压缩列表:哈希中元素数量小于512个;哈希中所有键值对的键和值字符串长度都小于64字节。如果有一个条件不满足,则使用哈希表;且编码只可能由压缩列表转化为哈希表,反方向则不可能

127.0.0.1:6379> hset std name xiaoming
  (integer) 1
  127.0.0.1:6379> object encoding std
  "ziplist"
  127.0.0.1:6379> hset std year 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
  (integer) 1
  127.0.0.1:6379> object encoding std
  "hashtable"

集合

集合和列表类似,但是集合不能存在重复元素,并且是无序的。一个集合中最多可以存储2^32-1个元素;除了支持常规的增删改查,Redis还支持多个集合取交集、并集、差集。

集合的内部编码可以是整数集合(intset)或哈希表(hashtable)。哈希表和前面哈希一样,暂不解释,需要注意的是,集合在使用哈希表时,值全部被置为null。

intset结构如下所示:

typedef struct intset{
      uint32_t encoding;
      uint32_t length;
      int8_t contents[];
  } intset;

其中,encoding代表contents中存储内容的类型,虽然contents(存储集合中的元素)是int8_t类型,但实际上其存储的值是int16_t、int32_t或int64_t,具体的类型便是由encoding决定的;length表示元素个数。

整数集合适用于集合所有元素都是整数且集合元素数量较小的时候,与哈希表相比,整数集合的优势在于集中存储,节省空间;同时,虽然对于元素的操作复杂度也由O(1)变为了O(n),但由于集合数量较少,因此操作的时间并没有明显劣势。

编码转换测试:

127.0.0.1:6379> object encoding std
  "hashtable"
  127.0.0.1:6379> sadd set1 name year sex
  (integer) 3
  127.0.0.1:6379> object encoding set1
  "hashtable"
  127.0.0.1:6379> sadd set2 12 13 14
  (integer) 3
  127.0.0.1:6379> object encoding set2
  "intset"
  127.0.0.1:6379> sadd set3 13 15 year
  (integer) 3
  127.0.0.1:6379> object encoding set3
  "hashtable"

有序集合

有序集合和集合一样是不可重复的元素,但是有序集合中的元素是有顺序的。与列表使用索引下标作为排序依据不同,有序集合为每个元素设置一个分数(score)作为排序依据。

有序集合的内部编码可以是压缩列表(ziplist)或跳跃表(skiplist)。跳跃表是一种有序的数据结构,一种特殊的有序链表,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表结构如下图所示:

redis get编码 redis编码字符集_redis get编码_03

1、跳跃表是由多层有序链表组合而成的,最底一层的链表保存了所有的数据,每向上的一层链表依次保存了下一层链表的部分数据。

2、相邻的两层链表中元素相同的节点之间存在引用关系,一般是上层节点中存在一个指向下层节点的引用。

3、跳跃表的目的在于提高了查询效率,同时也牺牲了一定的存储空间。

只有同时满足下面两个条件时,才会使用压缩列表:有序集合中元素数量小于128个;有序集合中所有成员长度都不足64字节。如果有一个条件不满足,则使用跳跃表;且编码只可能由压缩列表转化为跳跃表,反方向则不可能

编码转换测试:

127.0.0.1:6379> zadd zset1 1 zheng 2 zhao 3 zhang 
  (integer) 3
  127.0.0.1:6379> object encoding zset1
  "ziplist"
  127.0.0.1:6379> zadd zset1 4 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
  (integer) 1
  127.0.0.1:6379> object encoding zset1
  "skiplist"

5种类型编码总结

redis get编码 redis编码字符集_Redis_04

好了,以上就是全部内容了,我是小鱼仙,你们的学习成长小伙伴

我希望有一天能够靠写字养活自己,现在还在磨练,这个时间可能会有很多年,感谢你们做我最初的读者和传播者。请大家相信,只要给我一份爱,我终究会还你们一页情的。

哦,对了!后续的更新文章我都会及时放到这里,欢迎大家点击观看,都是干货文章啊,建议收藏,以后随时翻阅查看