在 redis 中,并没有直接使用前面所介绍的那些数据结构(动态字符串,压缩列表,字典)来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,也就是说,用对象来封装了这些底层的数据结构。
有五种对象:字符串对象、列表对象、哈希对象、集合对象和有序集合对象,每种对象至少用到了一种前面介绍过的数据机构。
使用对象的好处是:
- redis 在执行命令之前,根据对象的类型来判断它是否可以执行给定的命令。
- 针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。即,redis可以根据不同的场景来为一个对象设置不同的编码(使用不同的数据结构来存储数据)。例如,元素较少时,使用
压缩列表
作为列表对象的底层实现;元素变多时,转换成适合大量元素的双端列表
。 - 实现了基于
引用计数
的内存回收机制和对象共享机制。
redis 中每个对象都由一个 redisObject 结构表示:
typedef struct redisObject {
// 类型,五种对象之一
unsigned type: 4;
// 编码,底层实现
unsigned encoding:4;
// 指向底层实现数据结构的指针
void* ptr;
} robj;
字符串对象的编码可以是 int、raw 或者 embstr。 embstr 编码是专门用于保存短字符串的一种优化编码方式,这种编码和 raw 编码(对应底层为sds,简单动态字符串)一样,也是使用 sdshdr 结构表示字符对象。不同的是,raw 编码会调用2次内存分配来创建 redisObject 和 sdshdr 结构,而 embstr 编码只调用一次,分配一块连续的空间。 2中编码方式内存结构如下:
列表对象的编码可以是 ziplist 或者 linkedlist。 图示如下:
从上图中可以看出,linkedlist 中嵌套了字符串对象
,字符串对象
是redis
五种类型中唯一一种会被其他四种类型对象嵌套的对象。
** 何时使用 ziplist
编码**:
- 字符串的长度小于64字节
- 列表中元素个数小于512个
上述的阈值是默认的,可以修改
哈希对象的编码可以是ziplist
或者 hashtable
。
ziplist 编码的哈希对象
+-----------------------+
| redisObject |
+-----------------------+
| type |
| REDIS_HASH |
+-----------------------+
| encoding |
|REIDS_ENCODING_ZIPLIST |
+-----------------------+ +----------+
| ptr |--> | 压缩列表 |
+-----------------------+ +----------+
| ... |
+-----------------------+
压缩列表
的底层实现示意图:
+-------+-------+-------+-------+------+-------+---+-------+
|zlbytes|zltail | zllen | "name"| "Tom"| "age" | 25| zlend |
+-------+-------+-------+-------+------+-------+---+-------+
| | | |
+-------+ +-----+
| \
第一个添加的键值对 最后添加的键值对
示意图如下: ziplist 编码的哈希对象
+-----------------------+
| redisObject |
+-----------------------+
| type |
| REDIS_HASH |
+-----------------------+
| encoding |
| REIDS_ENCODING_HT |
+-----------------------+ +--------------+
| ptr |--> | dict |
+-----------------------+ +--------------+ +--------------+
| ... | | StringObject |-->| StringObject |
+-----------------------+ | "age" | | 25 |
+--------------+ +--------------+
| StringObject |
| "name" |-->+--------------+
+--------------+ | StringObject |
| "Tom" |
+--------------+
何时使用 ziplist
编码:
- 键和值 长度 < 64字节
- 键值对数量 < 512个
上述的阈值是默认的,可以修改