Redis读书笔记(二)
Redis对象系统
Redis对象
- 字符串(String)的底层实现方式
- 直接保存整数值:字符串对象保存的是整数值,且可以用long类型来表示。
- embstr编码的SDS:字符串对象保存的是一个长度小于等于39字节的字符串值。
- SDS:字符串对象保存的是一个长度大于39字节的字符串值。
- 列表(List)的底层实现方式
- 压缩列表:列表对象保存的所有字符串元素的长度都小于64字节,且元素数量小于152个(注意:上限值是可以修改的,其他对象的上限值也是同洋的道理)。
- 双端链表:不满足压缩列表的使用条件,则使用双端链表。
- 哈希(Hash)的底层实现方式
- 压缩列表:哈希对象保存的所有键值对的键和值的字符串长度都小于64字节,且键值对数量小于512个。
- 字典:不满足压缩列表的使用条件,则使用字典。
- 集合(Set)的底层实现方式
- 整数集合:集合对象保存的所有元素都是整数,且元素数量不超过512个。
- 字典:不满足整数集合的使用条件,则使用字典。
- 有序集合(Zset)的底层实现方式
- 压缩列表:有序集合保存的元素数量小于128个,且所有元素的长度都小于64字节。
- 跳跃表+字典:不满足压缩列表的使用条件,则使用跳跃表+字典
Redis对象的实现
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 指向底层数据结构的指针
void *ptr;
// 引用计数
int refcount;
// 最后一次被命令程序访问的时间
unsigned lru:8;
//...
} robj;
对象的类型属性Type
类型常量 | 对象的名称 |
---|---|
REDIS_STRING | 字符串对象 |
REDIS_LIST | 列表对象 |
REDIS_HASH | 哈希对象 |
REDIS_SET | 集合对象 |
REDIS_ZSET | 有序集合对象 |
对象的编码属性Encoding和底层实现
类型 | 编码 | 对象 |
---|---|---|
REDIS_STRING | REDIS_ENCODING_INT | 使用整数值实现的字符串对象 |
REDIS_STRING | REDIS_ENCODING_EMBSTR | 使用embstr编码的简单动态字符串实现的字符串对象 |
REDIS_STRING | REDIS_ENCODING_RAW | 使用Simple Dynamic String实现的字符串对象 |
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 | 使用跳跃表和字典实现的有序集合对象 |
为什么需要同时使用跳跃表和字典来实现有序集合?
- 只使用字典来实现:执行查找的时间复杂度为O(1),但是执行范围性查询操作如ZRANGE、ZRANK等,需要对字典保存的所有元素排序,至少需要O(NlogN)的时间,以及额外的O(N)空间。
- 只使用跳跃表来实现:执行范围性查询的优点会被保留,但执行查找的时间复杂度为O(logN)。
所以为了查找和范围性查询都有尽可能快的执行,同时使用字典和跳跃表实现。
内存回收
对象的引用计数会随着对象的使用状态而不断变化:
- 当创建一个新对象时,引用计数的值被初始化为1
- 当对象被一个新程序使用时,引用计数的值会加1
- 当对象不再被一个程序使用时,引用计数的值会减1
- 当对象的引用计数变为0时,对象所占的内存会被释放
对象共享
Redis中让多个键共享同一个值对象(节约内存):
- 将key的值指针指向一个现有的对象
- 将被共享的值对象的引用计数加1
Redis只对包含整数值的字符串对象进行共享:
当共享对象时,首先需要检查给定的共享对象和想创建的目标对象是否完全一致,只有在共享对象和目标对象完全相同的情况下才会使用共享对象。
- 如果共享的是保存整数值的字符串对象,那么验证是否一致的时间复杂度为O(1)
- 如果共享的是保存字符串的字符串对象,那么验证是否一致的时间复杂度为O(N)
- 如果共享的是包含了多个值的对象,那么验证是否一致的时间复杂度为O(N*2)
所以考虑到CPU时间的限制,Redis只对包含整数的字符串对象进行共享。