2 redis编码及数据结构
上面都是一些redis的基本入门级操作,接下来对于底层的分析是重点
2.1 redis支持的数据类型
2.2 Redis对象源码(3.0版)
redisObject类的属性有三个:
type:类型
encoding:对象的编码
int,embstr,raw,ht(哈希表),linkedlist,ziplist(连续空间),intset,skiplist(跳表)
ptr:指向底层实现数据结构的指针
type可以看做是第一层、encoding可以看做是第二层、ptr可以看做是第三层,他们之间有一些对应关系。
2.3 Redis对象类型——type(第一层)& encoding(第二层)
redis对象的key都是String的,value就是其支持的5种数据类型。我们可以用TYPE命令查看value的类型:
5个数据类型与源码中的8个encoding(编码)类型的对照关系(第一层和第二层对照):
之所以有这么多的对照关系是因为redis支持了针对不同应用场景来采用不同的数据结构,具有更大的灵活性。
我们可以用OBJECT encoding命令来查看相应的encoding类型:
2.4 字符串内部实现——int & raw & embstr
int编码很简单,ptr指向一个int型数据
raw的内部实现是ptr指向一个sds(可以简单的理解为redis中实现的字符串)。
free属性表示预留的空闲空间,len表示实际字符串的长度,buf是指向一个字符数组的指针。
预留空间:当申请的空间较小时,C语言在申请len这么多的空间时会申请一份同样大小的预留空间。
惰性空间释放:删除数据时,free的值会增加,而不是真正的把数据给释放掉
embstr结构和raw的类似,只不过ptr不是指向sds的指针了,而是一个连续空间,ptr后面紧跟着sds对象,所以查询会非常快,但这种方式并不适合存储大量数据。
选择编码格式的规则:
1.如果设置的数字是一个long类型的,那么其encoding就是int
2.如果不是long类型表示的整形数字,那么用raw或者embstr
(1)长度小于等于39则是embstr
(2)长度大于40则是raw
3.embstr是一个只读的,如果要修改这样类型的数据,修改之后的结果会变为raw
2.5 列表的内部实现——ziplist & linkedlist编码
zlbytes:记录整个列表占用的内存字节数
zltail:记录列表尾节点距离压缩列表的起始地址有多少字节
zllen:包含的节点数量
zlend:标记压缩列表的末端
linkedlist是一个双向链表,但头节点的前驱指针指向的是null,尾节点的后继指针指向的是null,不是一个环形链表
2.6 集合类型内部实现——intset & hashtable编码
ptr指向intset,inset中针对不同的应用有不同的编码,为了防止空间的浪费。
length表示contents对应的数组的长度
ptr指向一个dict(字典),并且这个dict的value部分都是null
当我们向set中添加整数类型的数据时,set用intset来实现,否则使用hashtable来实现的。此外,如果数据量较大,一般是超过512时也会使用hashtable
2.7 有序集合类型内部实现——ziplist & skiplist 编码
ziplist中的ptr指向一段连续的内存空间,所有的键值对紧密相连并从队尾推入,当我们zadd 122 bob 134 muse 156 john之后内存中的示意图就如上图所示。同样地,如果存储的数据元素个数超过一定的阈值的话,就会转换成skiplist
skiplist中有两种数据结构,跳表和哈希表(dictht),因为不同的业务逻辑无法用同一种数据结构来达到完美的效果。
跳表中的level对应的是maxlevel,即length最长的节点的层(L)数,上图一共有三个节点bob,muse和john对应length1,2,3。
跳表的查找过程:
如果要查找muse,先从L5往后查发现是john,又从L4开始查发现是bob和john,又从L2查到结果,因此level越大,查询的跨度越大
2.8 哈希类型内部实现——ziplist & hashtable 编码
和set类似,ziplist上面已经介绍过了就不再赘述,存储的键值对超过一定阈值时就转变为hashtable