0、对象
Redis 没有直接使用基本数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统:
- 字符串对象
- 列表对象
- 哈希对象
- 集合对象
- 有序集合对象
使用对象的优点:
- 可以在执行命令前,根据对象的类型来判断一个对象是否可以执行给定的命令。
- 可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化效率。
- Redis 的对象系统实现了基于引用计数技术的内存回收机制。
- Redis 的对象系统实现了基于引用计数技术的对象共享机制,这一机制可以在适当的条件下,通过让多个数据库共享同一个对象来节约内存。
- Redis 对象带有访问时间记录信息,在服务器启用了 maxmemory 功能的情况下,空转时长较大的键可能会优先被服务器删除。
对象定义
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 引用计数
int refcount;
unsigned lru;
// 指向底层实现数据结构的指针
void *ptr;
} robj;
类型
类型常量 | 对象的名称 | TYPE 命令的输出 |
---|---|---|
REDIS_STRING | 字符串对象 | “string” |
REDIS_LIST | 列表对象 | “list” |
REDIS_HASH | 哈希对象 | “hash” |
REDIS_SET | 集合对象 | “set” |
REDIS_ZSET | 有序集合对象 | “zset” |
编码
编码常量 | 编码所对应的底层数据结构 | OBJECT ENCODING 命令输出 |
---|---|---|
REDIS_ENCODING_INT | long 类型的整数 | “int” |
REDIS_ENCODING_EMBSTR | embstr 编码的 SDS | “embstr” |
REDIS_ENCODING_RAW | SDS | “raw” |
REDIS_ENCODING_HT | 字典 | “hashtable” |
REDIS_ENCODING_LINKEDLIST | 双端列表 | “linkedlist” |
REDIS_ENCODING_ZIPLIST | 压缩列表 | “ziplist” |
REDIS_ENCODING_INTSET | 整数集合 | “intset” |
REDIS_ENCODING_SKIPLIST | 跳跃表和字典 | “skiplist” |
类型与编码对象
类型 | 编码 |
---|---|
REDIS_STRING | REDIS_ENCODING_INT REDIS_ENCODING_EMBSTR REDIS_ENCODING_RAW |
REDIS_LIST | REDIS_ENCODING_ZIPLIST REDIS_ENCODING_LINKEDLIST |
REDIS_HASH | REDIS_ENCODING_ZIPLIST REDIS_ENCODING_HT |
REDIS_SET | REDIS_ENCODING_INTSET REDIS_ENCODING_HT |
REDIS_ZSET | REDIS_ENCODING_ZIPLIST REDIS_ENCODING_SKIPLIST |
1、字符串对象
编码
- int
- embstr(字符串长度小于等于 32 字节)
- raw(字符串长度大于 32 字节)
embstr 和 raw
embstr 编码是专门用于保存短字符串的一种优化编码方式。
两者都使用 redisObject 结构和 sdshdr 结构来表示字符串对象。
raw 编码会调用两次内存分配函数来分别创建 redisObject 结构和 sdshdr 结构。
embstr 编码调用一次内存分配函数来分配一块连续的空间,空间中依次包括 redisObject 和 sdshdr,连续的空间使得更好地利用缓存带来的优势。
释放内存时,同上。
编码转换
- int 编码的字符串对象和 embstr 编码的字符串对象在条件满足的情况下,会被转换为 raw 编码的字符串对象。
- embstr 编码的字符串没有修改程序,一旦被修改,就会转换成 raw 编码的字符串对象。
2、列表对象
编码
- ziplist
- linkedlist
编码转换
同时满足以下两个条件时,使用 ziplist 编码:
- 所有元素长度小于 64 字节
- 元素数量小于 512 个
list-max-ziplist-value
list-max-ziplist-entries
3、哈希对象
编码
- ziplist
- hashtable
编码转换
同时满足以下两个条件时,使用 ziplist 编码:
- 所有元素长度小于 64 字节
- 元素数量小于 512 个
hash-max-ziplist-value
hash-max-ziplist-entries
4、集合对象
编码
- intset
- hashtable
编码转换
同时满足以下两个条件时,使用 intset 编码:
-
所有元素都是整数值
-
元素数量小于 512 个
set-max-intset-entries
5、有序集合对象
编码
- ziplist
- skiplist
skiplist 编码的有序集合
使用 zset 结构作为底层实现。
typedef struct zset {
zskiplist *zsl;
dict *dict;
} zset;
// 两种数据结构通过指针来共享相同元素的成员和分值,所以不会浪费额外的内存。
编码转换
同时满足以下两个条件时,使用 ziplist 编码:
- 所有元素长度小于 64 字节
- 元素数量小于 128 个
zset-max-ziplist-value
zset-max-ziplist-entries
类型检查与命令多态
类型检查通过 redisObject 结构的 type 属性来实现。
命令多态通过 redisObject 结构的 encoding 属性来实现。
对象共享
Redis 会在初始化服务器时,创建一万个字符串对象,包含了从 0 到 9999 的所有整数值。创建字符串对象的数量可以通过 redis.h/REDIS_SHARED_INTEGERS 常量来修改。
Redis 只共享整数值的字符串对象。
查看对象的引用计数:object refcount key
对象的空转时长
查看对象的空转时长:object idletime key
如果服务器打开了 maxmemory 选项,并且服务器用于回收内存的算法为 volatile-lru 或者 allkeys-lru,当占用内存超过 maxmemory 时,空转时长较高的键会被服务器释放,从而回收内存。