Redis 对象


0、对象类型和编码

Redis 中的每个对象都由一个 redisObject 结构表示,该结构包含三个属性:type、encoding 和 ptr:

typedef struct redisObject {
  unsigned type: 4;     // 类型
  unsigned encoding: 4; // 编码
  void *ptr;            // 指向底层数据结构的指针
} robj;

type 包含5种类型:

  • 字符串对象:string
  • 列表对象:list
  • 哈希对象:hash
  • 集合对象:set
  • 有序集合对象:zset

encoding 记录对象使用的编码,即对象使用了什么数据结构

  • long 型的整数:REDIS_ENCODING_INT
  • embstr 编码的简单动态字符串:REDIS_ENCODING_EMBSTR
  • 简单动态字符串:REDIS_ENCODING_RAW
  • 字典:REDIS_ENCODING_HT
  • 双端链表:REDIS_ENCODING_LINKEDLIST
  • 压缩列表:REDIS_ENCODING_ZIPLIST
  • 整数集合:REDIS_ENCODING_INTSET
  • 跳跃表和字典:REDIS_ENCODING_SKIPLIST

使用 OBJECT ENCODING 命令可以查看数据库键的值对象编码。


通过 encoding 属性设置对象的编码,而不是把特定对象绑定一种固定编码,极大地提升了 Redis 的灵活性和效率,Redis 可以根据不同场景使用不同编码,优化在特定场景中的效率。

1、字符串对象

(1)编码

字符串对象的编码可以是 intraw 或者 embstr

  • 如果字符串对象保存的是整数值,且这个整数可以用 long 类型表示,字符串的编码将被设置为 int。
  • 如果字符串对象保存的是一个字符串,且长度大于32字节,编码设置为 raw。
  • 如果字符串对象保存的是一个字符串,且长度小于等于32字节,编码设置为 embstr。
(2)编码转换
  • embstr 编码的字符串是只读的。当对 embstr 编码的字符串进行修改时,程序要先将编码转换成 raw,然后再执行修改。

2、列表对象

列表对象的编码可以是 ziplist(压缩列表) 或者 linkedlist(双端链表)。

  • 列表中所有字符串长队都小于64字节;列表中的元素数量小于512个,列表的编码设置为 ziplist。
  • 以上任意条件不满足时,列表的编码就会转换为 linkedlist。

3、哈希对象

哈希对象的编码可以是 ziplist(压缩列表) 或者 hashtable(字典)。

(1)ziplist 编码

哈希对象保存的所有键值对的键和值的长度都小于64字节;元素数量小于512个,编码设置为 ziplist。

  • 保存了同一键值对的两个节点紧挨在一起,键在前值在后;
  • 先添加到哈希对象中的键值对放在压缩列表的表头方向,后添加的放在表尾方向。
(2)hashtable 编码

以上任意条件不满足时,编码设置为 hashtable。

  • 字典的每个键都是一个字符串对象,对象中保存了键值对的键;
  • 字典中每个值都是一个字符串对象,对象中保存了键值对的值。

4、集合对象

集合对象的编码可以试试 intset 或者 hashtable。

  • 集合对象保存的所有元素都是整数;元素数量小于512个,编码设置为 ziplist。
  • 否则,设置为 hashtable。当编码为 hashtable 时,每个键都是一个集合元素,值全部被设置为 null

5、有序集合对象

有序集合对象的编码可以是 ziplist 或者 skiplist。

(1)ziplist 编码

每个集合元素用两个紧挨在一起的压缩列表节点保存,第一个节点保存元素的成员,第二个保存元素的分值;元素按照分值从小到大排列。

(2)skiplist 编码

有序集合对象使用 zset 作为底层实现,一个 zset 同时包含一个字典和一个跳跃表;两种数据结构通过指针共享相同元素的成员和分值。

  • 每个跳跃表节点都保存一个集合成员:跳跃表节点的 object 属性保存元素成员score 属性保存元素的分值,通过跳跃表,程序可以对集合进行范围操作
  • 字典的保存了元素的成员,而保存了元素的分值,通过字典,程序可以快速查找分值
typedef struct zset {
  zskiplist *zst;
  dict *dict;
} zset;