Redis底层探秘(六):对象多态及回收
本篇是我们redis系列的最后一篇,整个系列其实是我学习《redis设计与实现》的笔记,这本书感觉不错,推荐使用redis的小伙伴都可以看看。
整个系列的文字都比较干,很多数据结构和C语言的东西,不过只有这么接近底层,我们才能知道redis为什么可以做到这么块。
类型检查与命令多态
redis中用于操作键的命令基本上可以分为两种类型。
其中一种命令可以对任何类型的键执行,比如说del命令、expire命令、rename命令、type命令、object命令等。
而另一种命令只能对特定类型的键执行,比如set、get、append、strlen等命令只能对字符串键键执行;hdel、hset、hget、hlen等命令只能对哈希键执行。
类型检查的实现
类型特定命令锁进行的类型检查是通过redisObject结构的type属性来实现的:
1 在执行一个类型特定命令之前,服务器会先检查输入数据库键的值对象是否为执行命令所需的类型,如果是的话,服务器就对键执行指定的命令;
2 否则,服务器将拒绝执行命令,并向客户端返回一个类型错误。
多态命令的实现
redis除了会根据值对象的类型来判断键是否能够执行指定命令之外,还会根据值对象的编码方式,选择正确的命令实现代码来执行命令。
举个例子,在前面介绍列表对象的编码时我们说过,列表对象有ziplist和linkedlist两种编码可用,其中前者使用压缩列表api来实现列表命令,而后者使用双端链表api来实现列表命令。
现在考虑这样一个情况,如果我们队一个键执行llen命令,那么服务器除了要确保执行命令的是列表键之外,还需要根据键的值对象所使用的编码来选择正确的llen命令实现:
1 如果列表对象的编码为ziplist,那么说明列表对象的实现为压缩列表,程序将使用ziplistLen函数来返回列表的长度;
2 如果列表对象的编码为linkedlist,那么说明列表对象的实现为双端链表,程序将使用listLength函数来返回双端链表的长度
借用面向对象方面的术语来说,我们可以认为llen命令是多态(polymorphism),只要执行llen命令的是列表键,那么无论值对象使用的是ziplist编码还是linkedlist编码,命令都可以正常执行。
内存回收
因为C语言并不具备自动内存回收功能,所以redis在自己的对象系统中构建了一个引用计数技术实现的内存回收机制,通过这一机制,程序可以通过跟中对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。
每个对象的引用计数信息由redisObject结构的refcount属性记录:
typedef struct redisObject{
// …
//引用计数
int refcount;
…….
}
对象的引用计数信息会随着对象的使用状态而不断变化:
1 在创建一个新对象时,引用计数的值会被初始化为1
2 当对象呗一个新程序使用时,他的引用计数值会被增一
3 当对象不再被一个程序使用时,他的引用计数值会被增一
4 当对象的引用计数值变为0时,对象所占用的内存会被释放
对象的整个生命周期可以划分为创建对象、操作对象、释放对象三个阶段。
对象共享
除了用于实现引用计数内存回收机制之外,对象的引用计数属性还带有对象共享的作用。举个例子,假设键A创建了一个包含整数值100的字符串对象作为值对象,如果这时键B也要创建一个同样保存了整数值100的字符串对象作为值对象,那么服务器有以下两种做法:
1 为键B新创建一个包含整数值100的字符串对象
2 让键A和和键B共享同一个字符串对象
很明显,第二种方法更节约内存,在redis中,让多个键共享一个值对象需要执行以下两个步骤
1 将数据库键的值指针指向一个现有的值对象、
2 将被共享的值对象的引用计数增一
目前来说,redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会使用这些共享对象,而不是新创建对象。(可以通过配置修改常量值)
另外,这些共享对象不单单只有字符串字符串键可以使用,那些在数据结构中嵌套了字符串对象的对象(linkedlist编码的列表对象、hashtable编码的哈希对象等)都可以使用这些共享对象。
延伸阅读:为什么redis不共享包含字符串的对象?
当服务器考虑将一个共享对象设置为键的值对象时,程序需要先检查给定的共享对象和键想创建的目标是否完全相同,只有在共享对象和目标对象完全相同的情况下,程序才会将共享对象用作键的值对象,而一个共享对象保存的值越复杂,验证共享对象和目标对象是否相同所需要的复杂度就会越高,消耗的cpu时间也会越多。
1 如果共享对象是保存整数值的字符串对象,那么验证操作的复杂度为o(1)
2 如果共享对象是保存字符串值的字符串对象,那么验证操作的复杂度为O(N)
3 如果共享对象对象是包含了多个值(或者对象的)对象,比如列表对象或者哈希对象,那么验证操作的复杂度将会是O(N^2)
因此,尽管共享更复杂的对象可以节约更多的内存,但受到cpu时间的限制,redis只对包含整数值的字符串对象进行共享。
对象的空转时长
除了前面介绍介绍过的type、encoding、ptr和refcount四个属性值之外,redisObject结构包含的最后一个属性为lru属性,该属性记录了对象最后一次被命令程序访问的时间,这个时间被利用在对象回收算法中。
对象回顾
1 redis数据库中的每个键值对的键和值都是一个对象
2 redis公有字符串、列表、哈希、集合、有序集合五种类型的对象,每种类型的对象至少都有两种或以上的编码方式,不同的编码可以在不同的使用场景上优化对象的使用效率。
3 服务器在执行某些命令之前,会先检查给定键的类型能否执行指定的命令,而检查一个键的类型就是检查键的值对象的类型。
4 redis的对象系统带有引用计数实现的内存回收机制,当一个对象不再被使用时,该对象所占用的内存就会被自动释放。
5 redis 会共享值为0到9999的字符串对象。
6 对象会记录自己的最后一次被访问的时间,这个时间可以用于计算对象的空转时间。
Redis底层探秘(六):对象多态及回收的更多相关文章
- Redis底层探秘(五):Redis对象
前面几篇文章,我们一起学习了redis用到的所有主要数据结构,比如简单动态字符串(sds).双端链表.字典.压缩列表.整数集合等等. redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这 ...
- Redis底层探秘(二):链表和跳跃表
链表简介 链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地跳转链表的长度. 作为一种常用数据结构,链表内置在很多高级的编程语言里面,因为Redis使用C语言并没有内 ...
- Redis底层探秘(一):简单动态字符串(SDS)
redis是我们使用非常多的一种缓存技术,他的性能极高,读的速度是110000次/s,写的速度是81000次/s.这么高的性能背后,到底是怎么样的实现在支撑,这个系列的文章,我们一起去看看. redi ...
- Redis底层探秘(三):字典
字典,又称为符号表(symbol table).关联数组(associative array)或映射(map),是一种用于保存键值对的抽象数据结构. 字典经常作为一种数据结构内置在很多高级编程语言里面 ...
- Redis底层探秘(四):整数集合及压缩列表
整数集合 整数集合(intset)是集合键的底层实现之一,当一个集合只包含 整数值元素,并且这个集合的元素数量不多时,Redis就会使用郑书记和作为集合键的底层实现. 整数集合的实现 整数集合是red ...
- redis 系列15 数据对象的(类型检查,内存回收,对象共享)和数据库切换
一. 概述 对于前面的五章中,已清楚了数据对象的类型以及命令实现,其实还有一种数据对象为HyperLogLog,以后需要用到再了解.下面再了解类型检查,内存回收,对象共享,对象的空转时长. 1.1 ...
- Redis 的底层数据结构(对象)
目前为止,我们介绍了 redis 中非常典型的五种数据结构,从 SDS 到 压缩列表,这都是 redis 最底层.最常用的数据结构,相信你也掌握的不错. 但 redis 实际存储键值对的时候,是基于对 ...
- redis底层设计(三)——redis数据类型
今天我们来看一下redis的数据类型.既然redis的键值对可以保存不同类型的值,那么很自然就需要对键值对的类型进行检查以及多态处理.下面我们将对redis所使用的对象系统进行了解,并分别观察字符串. ...
- redis底层实现的几种数据结构
redis底层数据结构 一.简单动态字符串(SDS) 定义: struct sdshdr{ int len; //SDS所保存的字符串长度 int free //记录buf数组中为使用的字节数量 ...
随机推荐
- gzframework开发记录
修改窗体权限: 重写方法 修改操作按钮名称 全部自定义增加操作按钮: 插入控件顺序: InsertAfterButton插入到指定控件后面 InsertBeforeButton插入到指定控件前面 公共 ...
- 配置NFS作为HDFS高可用的共享存储系统
所有命令或步骤: 首先,在各个节点上安装nfs服务 yum install -y nfs service rpcbind start service nfs start 配置开机自启动服务 chkco ...
- 【Flask】Sqlalchemy 外键
### 外键:使用SQLAlchemy创建外键非常简单.在从表中增加一个字段,指定这个字段外键的是哪个表的哪个字段就可以了.从表中外键的字段,必须和父表的主键字段类型保持一致.示例代码如下: from ...
- [POI2007]立方体大作战tet
题目 BZOJ 洛谷 做法 很巧妙的题,注意每种颜色只有两个 消除一种颜色,其实就是看中间有多少个没有被消除的块,这种动态距离问题显然能用树状数组解决 洛谷输出方案,暴力往下爬就行 My comple ...
- NoSQL数据库memcache和redis区别
在web后台发开面试中,经常会被问道memcache和redis的区别和使用情况. 其中memcache和redis都是基于内存存储的缓存系统,存储形式key--value键值对的形式. 区别: 1. ...
- MySQL数据库基本操作(三)
MySQL补充: mysql是关系型数据库,关系数据库,是建立在关系模型基础上的数据库,现实世界中的各种实体,以及实体之间的各种联系,均用关系模型(table)来表示.#关系模型就是指二维表格模型,因 ...
- Go Interface概念
简单地说 Interface是一组Method的组合,可以通过Interface来定义对象的一组行为.如果某个对象实现了某个接口的所有方法,就表示它实现了该借口,无需显式地在该类型上添加接口说明.In ...
- 【2039】checker(Dp)
我今天脑子貌似又好使了一点,可以做一做DP中的水题了. 这个题难度蓝色,纯属是做的人太少了虚高. 这个题很显然的是可以用一个顺序一个逆序这两个大水转移方程轻松转移出到达这个地方最少需要的棋子数量,然后 ...
- sqlserver ,镜像数据库,CDC,实时监控数据变化
1.数据库镜像配置 1)主机环境:计算机名称修改SQL1.xiaoping.com 添加用户sqluser 密码永不变,率属于administrators sqlserver安装时,将所有sqlser ...
- Caused by: org.apache.ibatis.binding.BindingException: Parameter 'parameter' not found.解决
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'company' not found. Available para ...