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)编码
字符串对象的编码可以是 int
、raw
或者 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;