写点什么

Redis 学习笔记 07:对象

发布于: 2021 年 01 月 19 日
Redis 学习笔记 07:对象

前面我们分六次陆续分享介绍了 六 种底层数据结构,不过 Redis 并没有直接使用这些数据结构来实现键值数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合这五种类型的对象,每个对象都使用到了至少一种前边讲的底层数据结构。


Redis 使用对象来表示数据库中的键和值,当我们在 Redis 的数据库中创建一个键值对时,我们至少创建两个对象,一个用作键值对的键对象,另一个对象用作键值对的值对象。


数据结构

Redis 的 redisObject 结构的定义如下所示。

typedef struct redisObject {	unsigned type:4; //类型	unsigned encoding:4; //编码	unsigned lru:LRU_BITS; 	int refcount;	void *ptr; // 指向底层实现数据结构的指针} robj;
复制代码

属性介绍:

  • type : 4bit unsigned int 类型,表示对象的类型,枚举值有五种,包括 REDISSTRING, REDISLIST, REDISHASH, REDISSET 和 REDIS_ZSET。可以使用 type {key}命令查看对象所属类型,type 命令返回的是值对象类型,键都是 string 类型。

  • encoding: 4bit unsigned int 类型,表示对象的编码类型,编码类型指定了对象底层实现的数据结构,枚举值一共有八种,包括 REDIS_ENCODING_INT,REDIS_ENCODING_EMBSTR,REDIS_ENCODING_RAW,REDIS_ENCODING_ZIPLIST,,REDIS_ENCODING_LINKEDLIST,REDIS_ENCODING_HT,REDIS_ENCODING_INTSET,REDIS_ENCODING_SKIPLIST。可以使用 object encoding {key} 命令查看对象所属编码。

  • lruLRU_BITS bit()的 unsigned int 类型,记录对象最后一次被访问的时间,当配置了 maxmemory 和 maxmemory-policy=volatile-lru | allkeys-lru 时, 用于辅助 LRU 算法删除键数据。可以使用 object idletime {key}命令在不更新 lru 字段情况下查看当前键的空闲时间。

  • refcount:记录当前对象被引用的次数,用于通过引用次数回收内存。使用 object refcount {key}获取当前对象引用。当对象为整数且范围在[0-9999]时,Redis 可以使用共享对象的方式来节省内存。具体细节见之后共享对象部分。

  • *ptr:如果是编码类型是 REDIS_ENCODING_INT,直接存储数据,否则表示指向数据的指针


同一类型的对象,由于使用场景和内容大小的不同,其底层实现需要不同的数据结构以优化对象在不同场景下的使用效率和内存占用,所以一种 type 对应的 encoding 至少有两种。


底层数据结构转换

编码类型转换在 Redis 写入数据时自动完成,这个转换过程是不可逆的,转换规则只能从小内存编码向大内存编码转换。


对象类型与编码映射关系:


类型检查和命令多态

redis 命令操作对象的命令分两种,第一种是只能对某种类型的对象执行的,第二种是能对多种类型的对象执行的。

执行第一种命令的时候,需要先检查 redisObject 对象的 type 属性,满足后再根据 redisObject 对象的 encoding 属性选择真正的底层实现命令(eg. 执行 LLEN 命令,若对象底层为压缩列表,使用 zipListLen 函数返回,若对象实现为双端链表,使用 listLength 函数返回)这其实就是一种基于 encoding 属性的多态。

而第二种命令,例如 del 等命令,是基于 type 属性的多态。


内存回收

采用引用计数机制,refcount 的值是 redisObject 对象被引用的次数,refcount 为 0 后,会在适当的时机进行内存回收。


对象的引用计数信息会随着对象的使用状态而不断变化:

  • 在创建一个新对象时,引用计数的值会被初始化为 1;

  • 当对象被一个新程序使用时,它的引用计数值会被+1;

  • 当对象不再被一个程序使用时,它的引用计数值会被-1;

  • 当对象引用计数值变为 0 时,对象所占用的内存会被释放。

对象共享

引用计数属性使得一个 redisObject 对象可以方便的被共享,从而节省内存空间。但是由于服务器将一个共享对象设置成键的值对象时,需要检查共享对象和要创建的值对象是否完全相同,所以 Redis 只共享包含整数值的字符串对象。该整数值的上限可以通过配置设定,默认是 9999。


对象空转时长

redisObject 的 lru 属性最后一次被命令程序访问的时间,对象的空转时长可以通过当前时间减去 lru 时间算出。空转时长是一些内存回收算法的一个衡量属性,redis 中,服务器打开了 maxmemmory 属性,且回收算法为 violatile-lru,allkeys-lru 时,空转时间较长的那部分键会被优先释放。


发布于: 2021 年 01 月 19 日阅读数: 19
用户头像

坚持分享接地气儿的架构技术文章! 2018.02.26 加入

同名微信公众号「架构精进之路」,专注软件架构研究,技术学习与职业成长!坚持原创总结、沉淀和分享,希望能带给大家一些引导和启发,感谢各位的支持(关注、点赞、分享)!

评论

发布
暂无评论
Redis 学习笔记 07:对象