在【Redis】深入理解 Redis 常用数据类型源码及底层实现(1.结构与源码概述)一文中我们了解到Redis有如下8种底层数据结构:
- SDS动态字符串
- 双向链表
- 压缩列表 ziplist
- 哈希表 hashtable
- 跳表 skiplist
- 整数集合 intset
- 快速列表 quicklist
- 紧凑列表 listpack
关于这些底层数据结构是如何构成我们熟悉的Set、Sorted Set 、List、Hash......的Redis和7并不完全一致,我们来看看两个版本之间的区别
Redis 6.0.5
- String -> 动态字符串
- Set -> 哈希表+整数数组
- Sorted Set -> 跳表+压缩列表zipList
- List -> 双端链表+压缩列表zipList
- Hash -> 哈希表+压缩列表zipList
Redis 7 以后
2021.11.29以后,Redis 7 以后,没有ziplist了(ziplist被listpack取代)
- Hash -> 哈希表+紧凑列表listpack
- List -> quiecklist
- Sorted Set -> 跳表+紧凑列表listpack
- Set -> 哈希表+整数数组
- String -> 动态字符串
redisObject
操作的底层定义位于server.h
文件(Redis启动时就会加载server.
),如下图
每个键值对都会有一个dictEntry
以set hello world
为例,因为Redis是KV键值对的数据库,每个键值对都会有一个dictEntry
,dictEntry
表示哈希表节点的结构,指针*key
(下图红
色框框部分)、指针*value
(下图黄色框框部分),指针*next
指向下一个dictEntry
,dictEntry
对象的声明在dict.c
文件中,如下图:
其中*key
指向String
对象,*value
既能指向String
对象也能指向其他集合对象(如List
、Set
、ZSet
、Hash
),键值对的时间复杂度是O(1)
,值得注意的是,这里指向的都是Redis内部的抽象对象(redisObject
)
redisObject结构的作用
Redis定义了redisObject结构体来表示string、hash、list、set、zset等数据类型,即Redis每个对象都是一个redisObject结构**
redisObject
对象的声明位于server.h
文件内,如下图:
c
struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
int refcount;
void *ptr;
};
为了便于操作,Redis采用了redisObject
结构来统一5种不同的数据类型(下图绿色部分),这样所有的数据类型就可以以相同的形式在函数间传递而不用使用特定的类型结构。同时,为了识别不同的数据类型,redisObject
中定义了type
和encoding
两个字段对不同的数据类型加以区别。
简单理解,redisObject
就是string
、hash
、list
、set
、zset
的父类,可以在函数间传递时隐藏具体的类型信息。
我们依次解释下redisObject
中的各个参数:
-
type
:对象的类型,4位(5种)OBJ_STRING
、OBJ_LIST
、OBJ_HASH
、OBJ_SET
、OBJ_ZSET
- 使用
type xxx
命令进行查看
-
encoding
:当前值对象底层存储的编码类型,4位,即表示该类型的物理编码方式(同一种数据类型可能有不同的编码方式,比如String就提供了0、1、8这三种编码方式)- 使用
object encoding xxx
命令进行查看
- 使用
-
lru
:清除算法,lru
表示当内存超限时采用LRU算法清除内存中的对象(24位,如果是LRU则记录对象最后一次被程序访问的时间(与内存回收相关)? -
refcount
:引用计数,当refcount
为0时,表示该对象已经不被任何对象引用(可以被当作垃圾回收了) -
*ptr
:指向真正的底层数据结构的指针*ptr
指向的底层数据结构有:SDS、双向链表、压缩列表、哈希表、跳表、整数集合等(指向下图中的黄色部分的箭头)
我们来举个例子,对于一条指令set age 17
,当执行这条指令时,底层机会生成一个redisObject
对象,这个对象中的type
是String
;encoding
是int
;lru
记录的是最近被访问时间等(如下图)