Redis对象 -- redisObject
Redis有主要以下数据结构,比如简单动态字符串、双端链表、字典、压缩列表、整数集合。
Redis没有直接用这些数据来直接实现键值对的数据库,而是在这些数据结构之上又包装了一层RedisObject(对象),RedisObject 有五种对象:字符串对象、列表对象、哈希对象、集合对象和有序集合对象。
typedef struct redisObject {
// 类型 unsigned type:4;
// 编码 unsigned encoding:4;
// 对象最后一次被访问的时间 unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数 int refcount; // 指向实际值的指针 void *ptr;
} robj;
1.type类型:对于Redis键值对来说,键是一个字符串对象,而值可以是这五种对象的其中一种。
2。ptr指针:指向对象的底层实现数据结构;
3.encoding:表示ptr指向的数据结构,就是对这个对象使用了什么数据结构作为底层实现。
4.refcount
refcount表示引用计数,由于c语言不具备内存回收功能,所以Redis在自己的对象系统中添加了这个属性,当一个对象的引用计数为0时,则表示该对象已经不被任何对象引用,则可以进行垃圾回收了。扩展一下:Java中由于引用计数法解决不了循环引用的问题,所以 Java 中使用了可达性分析算法。那么 Redis 有没有考虑循环引用的问题呢?
5.lru表示对象最后一次被命令程序访问的时间。
五种对象对应的RedisObject
字符串对象(String)
字符串对象的encoding有三种分别是: int,raw,embstr。
1.如果一个字符串对象保存的是整数,并且这个整数可以用long类型标识,那么字符串对象会讲整数值保存在ptr属性中,并将encoding设置为int。加入有一个对象“10086”
2.如果一个字符串,并且字符串的长度大于32个字节,那么对象会使用一个简单动态字符串(SDS)来保存这个字符串,并将对象的编码设置为raw。字母的会使用一个Raw
3.如果字符串保存的是一个字符串值,并且字符串长度小于等于32字节,那么字符串对象将使用embstr编码的方式来保存这个字符串。
既然有了raw的编码方式,为什么还会有embstr编码方式呢?
因为embstr的编码方式有一些优点,如下:
- embstr 编码将创建字符串对象所需的内存分配次数从 raw 编码的两次降低为一次。
- 释放 embstr 编码的字符串对象只需要调用一次内存释放函数,而释放 raw 编码的字符串对象需要调用两次内存释放函数。
- 因为 embstr 编码的字符串对象的所有数据都保存在一块连续的内存里面,所以这种编码的字符串对象比起 raw ,编码的字符串对象能够更好地利用缓存带来的优势。
哈希对象(hash)
哈希对象编码有两种,分别是:ziplist、hashtable。
当哈希对象保存的键值对数量小于512,并且所有键值对长度都小于64字节时,使用压缩列表;负责使用hashtable存储。
哈希对象的压缩列表对应的十一图如下:
哈希对象的 hashtable 对应的示意图如下
列表对象(list)
列表对象的编码有两种,分别是ziplist、linkedlist。
ziplist(压缩列表)主要是为节省内存而设计的内存结构,它的优点就是节省内存,但缺点就是比其他结构要消耗更多的时间,所以 Redis 在数据量小的时候使用压缩列表存储。
当列表的长度小于 512,并且所有元素的长度都小于 64 字节时,使用压缩列表存储;否则使用 linkedlist 存储。
压缩列表示意图:
列表对象链表示意图:
集合对象(set)
集合对象的编码方式
String类型要存储一个 图片的id和图片的位置信息,比如要存储一个id为11110002234,位置信息为129394780011,这样一个信息,redisobject对象分配了8个字节,指针部分分配了8个字节,每一个图片地址有16字节,下来是32 字节,哈希表的每一项是一个dicEntry的结构体,用来指向一个键值对。dicEntry结构中有三个8字节的指针,分别指向key、value以及下一个dicEntry,三个指针共24字节。redis会申请32字节的空间。