《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

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-09-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java技术栈

一张图搞清楚Java异常机制

下面是Java异常类的组织结构,红色区域的异常类表示是程序需要显示捕捉或者抛出的。 ? Throwable Throwable是Java异常的顶级类,所有的异常...

2785
来自专栏开发技术

排序之简单选择排序

  本篇博客是在伍迷兄的博客基础上进行的,其博客地址点击就可以进去,里面好博客很多,我的排序算法都来自于此;一些数据结构方面的概念我就不多阐述了,伍迷兄的博客中...

892
来自专栏mySoul

Java静态方法和实例方法 java中的数组作为形参传入

启动一个Java程序的时候,会诞生一个虚拟机实例,当程序关闭退出时,该实例会消失。

791
来自专栏西枫里博客

Python学习笔记九(变量作用域及内置函数和闭包函数)

在上次的学习中,初步认识了Python的自定义函数方式及变量参数。那么编程中的局部变量和全局变量应该是大多数语言的标配。Python中如果定义局部变量和全局变量...

722
来自专栏swag code

多线程的实现方法

      从Thread类中实例化的对象即代表线程,启动一个线程就是建立一个Thread实例。因为完成线程真正功能的代码放在类的run()方法中,所以可以将线...

542
来自专栏我是业余自学C/C++的

二维变长数组

1185
来自专栏炉边夜话

GCC内嵌汇编语言

绝大多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语言,这些汇编代码都是 Intel 风格的。但在 Unix 和 Linux 系统中,更多...

1682
来自专栏Python小屋

Python编写只允许实例化一个对象的类

>>> class T: __total = 0 def __init__(self, value): if T.__total != 0: r...

3208
来自专栏数据结构与算法

1750:全排列

1750:全排列 查看 提交 统计 提问 总时间限制: 1000ms 内存限制: 65536kB描述 给定一个由不同的小写字母组成的字符串,输出这个字符串的所...

35614
来自专栏Golang语言社区

Golang语言的函数调用信息

函数的调用信息是程序中比较重要运行期信息, 在很多场合都会用到(比如调试或日志). Go语言 runtime 包的 runtime.Caller / runti...

4686

扫码关注云+社区