《Redis设计与实现》读书笔记(十) ——Redis对象相关其他设计与实现
《Redis设计与实现》读书笔记(十) ——Redis对象相关其他设计与实现
(原创内容,转载请注明来源,谢谢)
一、类型检查与命令多态
redis对键操作的命令分为两种,一种是可以对任意键进行操作的命令,如del、expire、rename、type、object等;另一种是只能对特定类型的键进行操作,redis的五种数据类型都有各自特定的键操作命令。
1、类型检查
redis的类型检查,是通过检查redisObject结构的type属性进行的。在执行一个特定命令之前,redis会先检查键对应的值对象的type属性,是否可以执行所需的命令:如果可以执行则执行;否则服务器将拒绝执行,并返回一个类型错误。
2、命令多态
redis每种数据类型,至少都有两种编码方式,命令多态就是指通过一个命令,可以执行不同的编码方式所提供的api。因此,redis的几乎每种操作命令都是多态的。对于同一个类型,操作不同的编码方式,称为基于编码的多态。
另外,还有上述提到的del、expire等,对于任意类型都可以操作,也是多态。这个是基于类型的多态。
二、内存回收
由于实现redis的C语言,不具备垃圾回收功能,因此redis构建了一套内存回收机制,是基于引用计数技术实现的内存回收。通过这一机制,程序可以通过追踪对象的引用计数信息,在适当的时候自动释放对象,并进行内存回收。
引用计数信息是redisObject结构中的refcount属性进行记录。
typedef structredisObject{
int refcount;
}robj;
上面的结构中省略结构中的其他信息。对象引用计数的信息,会随着对象使用状态的变化而改变,如下:
1)创建一个新对象时,refcount值是1。
2)对象被一个新程序使用,refcount值加1。
3)对象不再被某个程序使用,refcount值减1。
4)对象引用计数变成0时,对象所占的内存会被释放。
修改引用计数的api如下:
在对象的整个生命周期,可以分为创建对象、操作对象、释放对象。
三、对象共享
对象的引用计数属性,除了用于内存回收,还可以用于对象的共享。当多个键保存同一个值的时候,且值是整数类型的字符串对象时,redis会使用对象共享,让键指向同一个值。过程如下:
1)将数据库键的值指针指向一个现有的值对象。
2)将被共享的值对象的引用计数增1。
两个对象公用一个值的情况如下图:
对象共享机制对于节约内存有很大的帮助。redis初始化服务器的时候,会创建一万个字符串对象,是从0~9999的整数值,当服务器要用到这些值的时候,会使用共享对象。
共享对象的默认数量,可以通过修改文件redis.h中的常量REDIS_SHARED_INTEGERS进行修改。
因此,当如果创建了两个对象,值都是100,实际上就有三个对象引用,包括两个客户端创建的,和一个服务端持有的,如下图:
除了单独的字符串对象类型,在其他对象类型中,嵌套的字符串对象,也是会共享的。
另外,redis只共享整数类型的字符串对象,不共享字符串类型的字符串对象,是因为共享的对象如果是字符串,则比较字符串是否相同的过程比较耗时;同理,不把字符串对象以外的其他四种对象共享,也是为了避免对比带来的耗时。
四、对象空转时长
redisObject的最后一个属性——lru属性,是用于记录对象的最后一次被访问的时间。
typedef structredisObject{
unsigned lru:22;
}robj;
object idletime命令,可以打印出给定键的空转时长,这一时长是通过当前时间减去lru属性的值确定的。同时,该命令也具有特殊性。其他命令操作键,都会修改键的lru,而object idletime命令仅仅通过查询键的lru计算空转时长,并不修改lru。
如果配置中开启了maxmemory,并且服务器的回收算法设置为volatile-lru或者allkeys-lru,当服务器的内存超过了maxmemory所设定的上限,空转时长较高的键会被优先释放,从而回收内存。
五、redis对象总结
1、redis数据库的每个键和值都是一个redisObject对象。
2、redis有字符串、哈希、列表、集合、有序集合五种对象类型,每种对象类型至少2中编码方式(其中字符串对象有3中编码方式),不同的编码方式在不同的场景中具有高效的特定。
3、服务器执行某些命令之前,会先进行类型检查,对键进行类型检查,是检查键对应的值的类型。
4、redis具有对象的引用回收机制,当对象没有被使用,内存将被回收。
5、redis会共享0~9999共1万个整数值的字符串对象,这个数目可以通过配置文件修改。
6、redis会记录对象的最后访问时间,可以用来计算对象的空转时间。
——written by linhxx 2017.09.02
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【一天一大 lee】环形链表II (难度:中等) - Day20201010
- 【西法带你学算法】一次搞定前缀和
- 一文快速入门分库分表中间件 Sharding-JDBC (必修课)
- 求求你别再用System.out.println 了!!
- 为什么阿里巴巴Java开发手册中强制要求超大整数禁止使用Long类型返回?
- 独家 | 教你用Python来计算偏差-方差权衡
- 使用 KinD 加速 CI/CD 流水线
- 使用 Docker 加速开发工作流
- Kubernetes CRD 自定义控制器
- 推荐算法之: LFM 推荐算法
- 推荐算法之: DeepFM及使用DeepCTR测试
- Cypress系列(61)- 断言最佳实践
- Cypress系列(62)- 改造 PageObject 模式
- CPU 执行程序的秘密,藏在了这 15 张图里
- Cypress系列(63)- 使用 Custom Commands