redis数据类型的底层实现
作者:向前的步伐 / 发表: 2021年12月25日 22:46 / 更新: 2021年12月25日 23:55 / redis / 阅读量:694
一、简介
redis是一种key-value型的内存数据库,它所有的key都是字符串,而value常见的数据类型有五种:string、list、set、zset、hash。redis的这些数据结构, 在底层都是使用redisObject来进行表示。redisObject中有三个重要的属性, 分别是type、 encoding 和 ptr。
二、对象类型与编码
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;
......
}
对象的 ptr 指针指向对象的底层实现数据结构, 而这些数据结构由对象的 encoding 属性决定。
encoding 记录了这个对象使用了什么数据结构作为对象的底层实现。
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 | 使用跳跃表和字典实现的有序集合对象 |
三、string
字符串对象的 encoding 有三种,分别是:int、raw、embstr。
- 1、字符串对象,如果保存整数。void则指向long,编码设置为int。
- 2、如果是字符串会根据字符串长度判断编码方式。(sds len属性)
- 3、对于浮点数的保存也是使用sds,当作字符串值来保存的。编码类型为embstr或raw。
长度大于39个字节,那么数据结构为SDS,编码为raw。
如果是字符串,且小于39个字节,将使用embstr编码保存。
embstr是一种优化编码。虽然也使用SDS数据结构,但它只调用一次内存分配,比raw少调用一次。它调用一次内存分配,直接分配一块连续的空间, 空间中依次包含 redisObject 和 sdshdr 两个结构。
四、hash
哈希对象的编码有两种,分别是:ziplist、hashtable。
当哈希对象可以同时满足以下两个条件时, 哈希对象使用 ziplist 编码:
- 哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;
- 哈希对象保存的键值对数量小于 512 个;
数量小于 512,且键值对长度都小于 64 字节,使用ziplist(压缩列表)存储
key和value是先后被push到表尾的。key与value紧挨着,同时每对key-value也紧挨着。(key与value都是StringObject)
数量不小于 512或键值对长度有达到 64 字节,使用的是hashtable编码。
五、list
列表的 encoding 有两种,分别是:ziplist、linkedlist。
当列表对象可以同时满足以下两个条件时, 列表对象使用 ziplist 编码:
- 列表对象保存的所有字符串元素长度小于64字节;
- 列表对象保存的元素数量小于512个;
长度小于 512,且所有元素的长度都小于 64 字节,使用ziplist编码
长度不小于 512或元素的长度有达到 64 字节,使用linkedlist编码
六、set
集合的 encoding 有两种,分别是:intset、hashtable。
当集合对象可以同时满足以下两个条件时, 集合对象使用 intset 编码:
- 集合对象保存的所有元素都是整数值;
- 集合对象保存的元素数量不超过 512 个;
数量小于 512,且所有元素都是整数值,使用intset编码
数量不小于 512或所有元素不全是整数值,使用hashtable编码
key(StringObject)保存元素值,value为NULL。
七、zset
zset的编码有两种,分别是:ziplist、skiplist。
当有序集合对象可以同时满足以下两个条件时, 有序集合对象使用 ziplist编码:
- 有序集合对象保存的所有元素的长度都小于 64 字节;
- 有序集合对象保存的元素个数小于128个;
个数小于 128,且所有元素的长度都小于 64 字节,使用ziplist编码
个数不小于 128或元素存在长度达到 64 字节,使用skiplist编码
skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。
typedef struct zset {
zskiplist *zs1
dict *dict
}
zsl 跳跃表按分值从小到大保存了所有集合元素。
每个跳跃表节点都保存了一个集合元素: 跳跃表节点的 object 属性保存了元素的成员, 而跳跃表节点的 score 属性则保存了元素的分值。
dict 字典为有序集合创建了一个从成员到分值的映射。