Redis对象类型编码(补充内存篇)
来源:左耳君(ID:qaqzuoer)
作者: 湿兄
上一篇我们说到了内存篇,但是关于内存还有一些东西要了解,就是关于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结构组成
双端链表保存了头节点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结构组成。哈希表如下所示,不详解
只有同时满足下面两个条件时,才会使用压缩列表:哈希中元素数量小于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)。跳跃表是一种有序的数据结构,一种特殊的有序链表,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表结构如下图所示:
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种类型编码总结