redis对象
基本概念
-
redis的对象就是在各种底层结构体的基础上, 根据具体应用场景做出不同实现时应遵循的接口
-
redis存储的键值对至少包含两个对象: 键对象(只能是字符串对象) 和 值对象
-
// 对象的结构 typedef struct redisObject { unsigned type:4; // 类型 unsigned encoding:4; // 编码 void *ptr; // 指向底层实现数据结构的指针 // ... } robj;
-
type的值是枚举类型, 有以下六类
- REDIS_STRING
- REDIS_LIST
- REDIS_HASH
- REDIS_SET
- REDIS_ZSET
-
encoding的值也是枚举类型, 有以下八类
-
编码常量 编码所对应的底层数据结构 REDIS_ENCODING_INT
long
类型的整数REDIS_ENCODING_EMBSTR
embstr
编码的简单动态字符串REDIS_ENCODING_RAW
简单动态字符串 REDIS_ENCODING_HT
字典 REDIS_ENCODING_LINKEDLIST
双端链表 REDIS_ENCODING_ZIPLIST
压缩列表 REDIS_ENCODING_INTSET
整数集合 REDIS_ENCODING_SKIPLIST
跳跃表和字典
-
-
所以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
使用跳跃表和字典实现的有序集合对象。
-
-
字符串对象
- 字符串对象负责实现对字符串和数字的存储
- 类型分别为 embstr(字符串 < 39字节)(只读), raw(字符串 > 39字节), int(整数)
- 浮点数在存储时会被转化为字符串类型, 在计算时再转化回来
具体实现
- 当存储整数时, 例如:
SET test 100
, 使redisobj对象
的指针ptr指向long类型整数 - 当使用embstr时, 会给
redisobj对象
和sds字符串
开辟一整块空间(短字符串限定优化) - 当使用raw时, 会给
redisobj对象
和sds字符串
分别开辟空间, 并使ptr指向sds
升级
- 向整数追加字符串时, 类型会从int转化为raw
- 修改只读短字符串时, 类型会从embstr转化为raw
列表对象
- 可以使用ziplist或者linkedlist实现
- ziplist
- 列表对象保存的所有字符串元素的长度都小于
64
字节; - 列表对象保存的元素数量小于
512
个;
- 列表对象保存的所有字符串元素的长度都小于
- linkedlist
- 其余情况
- ziplist
哈希对象
- 可以使用ziplist或hashtable实现
- ziplist
- 列表对象保存的所有字符串元素的长度都小于
64
字节; - 列表对象保存的元素数量小于
512
个; - 将键值对按先后顺序放入ziplist(list中的每两个entry构成一个键值对)
- 列表对象保存的所有字符串元素的长度都小于
- hashtable
- 其他情况
- ziplist
集合对象
- 可以使用intset或hashtable实现
- intset
- 集合对象保存的所有元素都是整数值;
- 集合对象保存的元素数量不超过
512
个;
- hashtable
- 其他情况
- intset
有序集合对象
-
可以使用ziplist或skiplist(实质是zset)
-
ziplist
- 有序集合保存的元素数量小于
128
个; - 有序集合保存的所有元素成员的长度都小于
64
字节; - 将成员-分值对按分值大小放入ziplist(list中的每两个entry构成一个成员-分值对)
- 有序集合保存的元素数量小于
-
zset
-
typedef struct zset { zskiplist *zsl; // 主要使用在批量操作连续元素时 dict *dict; // 主要使用在单独查询时 } zset;
-
zsl按分值大小顺序保存所有的有序集合元素
-
dict散列有序集合元素, 并以键值对的形式保存元素-分值对
-
同时使用zskiplist和dict实现zset是为了提高性能
-
指针形式保证了不会有重复的空间开辟, 而且可以根据不同的场景查不同的表
-
-
其它基本概念
类型检查与命令多态
- redis的命令分为两种
- 可以对任何对象使用
- 只能对特定类型的对象使用
- 在执行命令前检查redisobj的type属性进行校验
- redis的同一抽象命令在面对具体底层实现时会被相应转化(多态)
内存回收
typedef struct redisObject {
// ...
int refcount; // 引用计数
// ...
unsigned lru:22; // last recent used 最近一次被使用的时间
// ...
} robj;
- 正常回收机制
- 在创建一个新对象时, 引用计数的值会被初始化为
1
; - 当对象被一个新程序使用时, 它的引用计数值会被增一;
- 当对象不再被一个程序使用时, 它的引用计数值会被减一;
- 当对象的引用计数值变为
0
时, 对象所占用的内存会被释放。
- 在创建一个新对象时, 引用计数的值会被初始化为
- 开启maxmemory
- (当前时间 - lru) > maxmemory时, 会回收被使用的内存
对象共享
- redis在开启数据库时会创建0 ~ 9999的整数类型字符串对象
- 当需要存储这些值时, 直接将指针指向预创建的对象, 同时引用计数++
- 不仅仅是单纯存储整数时共享, 在存储其他复杂类型时也会共享这些整数
- 为什么不共享其他类型?
- 共享时需要检查是否完全相等, 其他类型的检查成本太高了