=================
== Time Stream ==
=================
一个小学生

Redis对象

redis

Redis对象系统包含:字符串对象String、列表对象List、哈希对象Hash、集合对象Set、有序集合对象ZSet。

  • Redis执行命令前可以根据对象类型判断一个对象是否可以执行给定的命令。
  • 可以针对不同使用场景为对象设置不同的数据结构实现,优化对象在不同场景下的使用效率。
  • Redis对象系统实现了基于引用计数计数的内存回收机制。
  • 还通过引用计数计数实现了对象共享机制,让多个数据库键共享同一个对象来节约内存。
  • 对象还带有访问时间记录信息,可以在启用了maxmemory功能时,优先删除一些键。

对象的类型和编码

Redis中的键和值都是对象,对象结构如下:

redisObject {
  // 类型
  unsigned type:4;
  
  // 编码
  unsigned encoding:4;
  
  // 指向底层实现数据结构的指针
  void * ptr;
  
  // lru,记录对象最后一次被访问的时间
  unsigned lru:22;
  
  // 引用计数
  int refcount;
  
  // ...
}

类型

  • 对于键,总是一个字符串对象
  • 对于值,类型可以是:字符串、列表、哈希、集合、有序集合对象。

对象的类型:

类型常量 对象名称
REDIS_STRING 字符串对象
REDIS_LIST 列表对象
REDIS_HASH 哈希对象
REDIS_SET 集合对象
REDIS_ZSET 有序集合对象

编码和底层实现

对象的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_SKIPSET 跳表和字典

字符串对象

字符串的对象编码可以是:

  • int,整数值
  • raw,字符串,长度大于32字节,使用SDS保存,编码设置为raw
  • embstr,字符串,长度小于32字节,使用embstr编码方式来保存字符串

embstr是专门用于保存短字符串的一种优化编码方式,embstr会一次内存分配来创建redisObject和sdshdr接口,而raw编码方式会调用两次分别创建。

编码转换

int编码的字符串和embstr编码的字符串可以抓换为raw编码的字符串对象。

列表对象

列表对象编码可以是:

  • ziplist,压缩列表,每个压缩列表节点entry保存一个列表元素。
  • linkedlist,双端链表,每个双端列表节点node保存了一个字符串对象,每个字符串对象都保存了一个列表元素。

编码转换

  • 列表对象保存的所有字符串元素长度都小于64字节,并且列表对象保存的对象元素数量小于512个,使用ziplist编码。
  • 其他的都是用linkedlist编码。

哈希对象

哈希对象的编码可以是:

  • ziplist,压缩列表,有新键值对加入到哈希对象,先将键的压缩列表节点推入压缩列表尾部,然后再将值的压缩列表节点推入压缩列表尾部。
  • hashtable,字典,哈希对象中每个键值对都是用一个字典键值对来保存。

编码转换

  • 哈希对象所有的键值对的键和值的字符串长度都小于64字节,并且哈希对象保存的键值对数量小于512个,使用ziplist编码
  • 其他的使用hashtable编码

集合对象

集合对象的编码可以是:

  • intset,整数集合
  • hashtable,字典,每个键都是一个字符串对象,每个字符串对象保存了一个集合元素,字典的值全部都是NULL

编码转换

  • 所有元素都是整数值,并且保存的元素数量不超过512个,使用intset编码
  • 其他的使用hashtable编码

有序集合对象

有序集合对象编码可以是:

  • ziplist,压缩列表,每个元素使用两个紧挨在一起的压缩列表节点来保存
  • skiplist,跳表,skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳表。字典查询快,跳表范围查找快。

编码转换

  • 有序集合保存数量小于128个并且所有元素长度都小于64字节,使用ziplist编码
  • 其他的使用skiplist编码

内存回收

使用引用计数技术实现内存回收机制,redisObject结构中有个refCount属性,用来记录对象的引用计数信息。

对象共享

引用计数属性还带有对象共享的作用。

Redis在初始化服务器时,创建一万个字符串对象,包含了0-9999所有整数值,服务器会使用这些共享对象。

对象的空转时长

redisObject结构中还包含一个lru属性,记录了对象最后一次被命令程序访问的时间。

如果服务器开了maxmemory选项,并且回收内存的算法为volatile-lru或者allkeys-lru,当服务器占用内存数超过了maxmemory值,空转时长较高的那部分键会被优先释放掉,从而回收内存。

参考

  • 《redis设计与实现》(第二版)