对象类型与编码
redis的key和value 都是对象。redis对象有五种:
类型常量 | 对象的名称 |
---|---|
REDIS_STRING | 字符串对象 |
REDIS_LIST | 列表对象 |
REDIS_HASH | 哈希对象 |
REDIS_SET | 集合对象 |
REDIS_ZSET | 有序集合对象 |
key总是字符串对象,value有可能是以上五种。
对象的内部结构
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 指向底层实现数据结构的指针
void *ptr;
// ...
} robj;
对象的 ptr 指针指向对象的底层实现数据结构, 而这些数据结构由对象的 encoding 属性决定。
encoding 记录了这个对象使用了什么数据结构作为对象的底层实现。
编码常量 | 底层数据结构 | OBJECT ENCODING 命令输出 |
---|---|---|
REDIS_ENCODING_INT | long 类型的整数 | “int” |
REDIS_ENCODING_EMBSTR | embstr 编码的简单动态字符串 | “embstr” |
REDIS_ENCODING_RAW | 简单动态字符串 | “raw” |
REDIS_ENCODING_HT | 字典 | “hashtable” |
REDIS_ENCODING_LINKEDLIST | 双端链表 | “linkedlist” |
REDIS_ENCODING_ZIPLIST | 压缩列表 | “ziplist” |
REDIS_ENCODING_INTSET | 整数集合 | “intset” |
REDIS_ENCODING_SKIPLIST | 跳跃表和字典 | “skiplist” |
字符串对象
编码: int | raw | embstr
1、字符串对象,如果保存整数。void则指向long,编码设置为int。
2、如果是字符串会根据字符串长度判断编码方式。(sds len属性)
长度大于39个字节,那么数据结构为SDS,编码为raw。
如果是字符串,且小于39个字节,将使用embstr编码保存。
embstr是一种优化编码。虽然也使用SDS数据结构,但它只调用一次内存分配,比raw少调用一次。
它调用一次内存分配,直接分配一块连续的空间, 空间中依次包含 redisObject 和 sdshdr 两个结构。
3、最后,redis对于浮点数的保存也是使用sds,当作字符串值来保存的。编码类型为embstr或raw。
列表对象
编码: ziplist | linkedlist
ziplist 数据结构:
linkedlist 底层实现数据结构:
它是一个双端链表,每个节点node保存了一个字符串对象。
字符串对象是一个非常基本的对象,它不仅被用作key,还会被其它结构(比如linkedlist的底层列表)嵌套。
列表对象使用ziplist编码时,保存的所有字符串元素长度小于64字节,且元素数量小于512个。
否则列表对象使用linkedlist编码。
以上两个值是可以通过配置文件进行修改的。
哈希对象
编码:ziplist | hashtable
ziplist 底层实现:
key和value是先后被push到表尾的。key与value紧挨着,同时每对key-value也紧挨着。(key与value都是StringObject)
hashtable 底层数据结构:
当哈希对象可以同时满足以下两个条件时, 哈希对象使用 ziplist 编码:
哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;
哈希对象保存的键值对数量小于 512 个。
以上两个值是可以通过配置文件进行修改的。
集合对象(set)
编码:intset | hashtable
intset底层数据结构:
底层实现是一个整数集合。
hashtable底层数据结构:
key(StringObject)保存元素值,value为NULL。
使用intset条件:
- 集合对象保存的所有元素都是整数值;
- 集合对象保存的元素数量不超过 512 个;
元素数量上限是可以修改的。
有序集合对象
编码: ziplist | skiplist(跳表)
ziplist 底层实现结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYEEoWRT-1587976902750)(http://redisbook.com/_images/graphviz-61b04c9bb72915ec0374125ba9455bc6783db4ff.png)]
每个集合元素使用两个紧挨在一起的压缩列表节点来保存, 第一个节点保存元素的成员(member), 而第二个元素则保存元素的分值(score)。
压缩列表内的集合元素按分值从小到大进行排序。
skiplist 底层实现:
使用zset,一个zset结构同时包含一个字典和一个跳表:
typedef struct zset {
zskiplist *zsl;
dict *dict;
} zset;
zsl 跳跃表按分值从小到大保存了所有集合元素。
每个跳跃表节点都保存了一个集合元素: 跳跃表节点的 object 属性保存了元素的成员, 而跳跃表节点的 score 属性则保存了元素的分值。
通过这个跳跃表, 程序可以对有序集合进行范围型操作, 比如 ZRANK 、 ZRANGE 等命令就是基于跳跃表 API 来实现的。
dict 字典为有序集合创建了一个从成员到分值的映射。
待。。