redis的对象包含5种对象:
- 字符串对象
- 列表对象
- 哈希对象
- 集合对象
- 有序集合对象
redis对象的好处
- 针对不同的使用场景,为对象设置多种不同的数据结构实现,优化对象不同场景下的使用效率.
- 对象实现了基于引用计数计数的内存回收机制
- 当redis不再使用某个对象的时候,这个对象所占用的内存就会被释放.
- redis还通过引用计数实现对象的共享机制,可以节约内存.
- 对象带有访问时间记录信息,可以通过该信息计算数据库键的空转时长,在服务器启用了maxmemory功能的情况下,空转时长较大的键会被服务器删除.
对象结构
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 对象最后一次被访问的时间
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向实际值的指针
void *ptr;
} robj;
-
类型 type
type 记录了对象的类型,可以是一下值重点任意一个- REDIS_STRING 字符串对象
- REDIS_LIST 列表对象
- REDIS_HASH 哈希对象
- REDIS_SET 集合对象
- REDIS_ZSET 有序集合对象
底层编码实现 encoding
对象的ptr指针指向对象底层实现的数据结构,这些数据结构由encoding来决定
编码常量 | 编码对应底层数据结构 |
---|---|
REDIS_ENCODING_INT | long类型整数 |
REDIS_ENCODING_EMBSTR | embstr编码的简单动态字符串 |
REDIS_ENCODING_RAW | 简单动态字符串 |
REDIS_ENCODING_HT | 字典 |
REDIS_ENCODING_LINKEDLIST | 双端链表 |
REDIS_ENCODING_ZIPLIST | 压缩列表 |
REDIS_ENCODING_INTSET | 整数集合 |
REDIS_ENCODING_SKIPLIST | 跳跃表和字典 |
每种类型(type)的对象至少使用两种不同的编码.
类型(type) | 编码(encoding) | 对象 |
---|---|---|
REDIS_STRING | REDIS_ENCODING_INT | 使用整数值实现的字符串对象 |
REDIS_STRING | REDIS_ENCODING_EMBSTR | 使用EMBSTR编码的简单动态字符串实现的字符串对象 |
REDIS_STRING | REDIS_ENCODING_RAW | 使用简单动态字符串实现的字符串对象 |
REDIS_LIST | REDIS_ENCODING_ZIPLIST | 使用压缩列表实现的列表对象 |
REDIS_LIST | REDIS_ENCODING_LINKEDLIST | 使用双端链表实现的列表对象 |
REDIS_HASH | REDIS_ENCODING_ZIPLIST | 使用压缩列表实现哈希对象 |
REDIS_HASH | REDIS_ENCODING_HT | 使用字典实现哈希对象 |
REDIS_SET | REDIS_ENCODING_INTSET | 使用整数集合实现集合对象 |
REDIS_SET | REDIS_ENCODING_HT | 使用字典实现集合对象 |
REDIS_ZSET | REDIS_ENCODING_ZIPLIST | 使用压缩列表实现有序集合对象 |
REDIS_ZSET | REDIS_ENCODING_SKIPLIST | 使用跳跃表和字典实现有序集合对象 |
字符串对象
底层实现
-
int
-
raw
-
embstr
raw 和 embstr的区别:
- raw调用两次内存分配函数来分别创建redisObject结构和sdshdr结构
- embstr调用一次内存分配函数来分配一块连续的空间,依次包含redisOject和sdshdr
列表对象
底层实现
-
ziplist
-
linkedlist
注意: linekedlist 编码的列表对象再地城的双端链表结构中包含了多个字符串对象,字符串对象是redis五中类型的对象中唯一一种会被其他4中类型对象嵌套的对象
编码转换
当列表对象可以同时满足以下两个条件时,列表对象使用ziplist.
- 列表对象保存的所有字符串元素的长度都小于64字节
- 列表对象保存的元素数量小于512个.
这两个值可以在配置文件中进行修改:
- list-max-ziplist-value
- list-max-ziplist-entries
哈希对象
底层实现
-
zipList
当有新的键值对要加入哈希对象时,程序会先姜保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入压缩列表结尾
-hashTable
编码转换
- 哈希对象保存所有键值对的键和值字符串长度不小于64字节
- 哈希对象保存的键值对数量小于512个
不满足这两个条件,哈希对象使用hashtable编码
集合对象
底层实现
-
intset
-
hashtable
编码转换
- 集合对象保存所有元素都是整数值
- 集合对象保存的元素数量不超过512个
不满足两个条件的集合对象要使用hashTable
有序集合对象
底层实现
-
ziplist
有序集合对象使用ziplist实现时,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,而第二个元素保存元素的分值.
skiplist
skiplist编码的有序集合对象使用zset结构作为底层实现.
/*
* 有序集合
*/
typedef struct zset {
// 字典,键为成员,值为分值
// 用于支持 O(1) 复杂度的按成员取分值操作
dict *dict;
// 跳跃表,按分值排序成员
// 用于支持平均复杂度为 O(log N) 的按分值定位成员操作
// 以及范围操作
zskiplist *zsl;
} zset;
zset结构中,跳跃表按照分值从小到大保存了所有集合的元素
zset结构中,dict字典为有序结合创建了一个从成员到分值的映射,字典的键保存了元素的成员,字典的值,保存了元素的分值
两种数据结构,通过指针来共享相同的元素成员和分值,不会浪费额外的内存.
注意: 图中为了方便展示,显示了各个元素的成员和分值,实际中,字典和跳跃表会共享元素的成员和分值
编码和转换
- 有序集合保存的元素数量小于128个
- 有序集合保存的所有元素成员长度小于64字节
对象的内存回收
Redis使用引用计数来实现内存回收机制
- 创建新对象时,引用计数的值会初始化为1
- 当对象被新程序使用时,引用计数j+1
- 当不被y9ige程序使用时,引用 -1
- 当引用计数为0 时,会被释放
对象共享
Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,服务器使用这些对象时,服务器会共享这些对象,而不是创建新的
对象空转时长
对象的lru属性,记录了对象最后一次被命令访问的时间
服务器打开了Maxmemory选项,并且服务器用于回事欧内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存超过了maxmemory选项所设置的上限值,空转时长较高的那部分键会被优先释放
总结
- redis 有五种对象
- 字符串对象
实现方式: int, embstr, raw - 列表对象
实现方式: ziplist,linkedlist - 哈希对象
实现方式: ziplist,hashtable - 集合对象
实现方式: intzet,hashtable - 有序集合对象
实现方式: ziplist,zset-{hashtable,skiplist}
- 字符串对象