zoukankan      html  css  js  c++  java
  • 我知道点redis-数据结构与对象(对象)-对象实现

    我知道点redis-数据结构与对象(对象)-对象实现

    8.7 类型检查与命令多态

    Redis中用于操作key 的命令基本上可以分为两种类型:

    1. 其中一种命令可以对任何类型的key 执行,比如DELEXPIRERENAME
    2. 另一种命令只能对特定类型的key 执行

    8.7.1 类型检查的实现

    为了确保只有指定类型的key可以执行某些特定的命令,在执行一个类型特定的命令之前,Redis会先检查输入key 的类型是否正确,然后在决定是否执行给定的命令。

    类型特定命令所进行的类型检查是通过redisObject结构的type类型来实现的。

    8.7.2 多态命令的实现

    Redis除了会根据值对象的类型来判断key 是否能后执行指定命令之外,还会根据value object的编码方式,选择正确的命令实现代码来执行命令。

    举个例子,在前面介绍列表对象的编码时我们说过,列表对象有ziplistlinkedlist两种编码可用,其中,牵着使用压缩列表的API来实现列表命令,而后者则使用双端链表API来实现列表命令。

    借用OO的术语来说,我们可以认为LLEN命令是多态(polymorphism)的,只要执行LLEN命令的是列表key ,那么无论对象使用的是ziplist编码还是linkedlist编码,命令都可以正常执行。

    实际上,我们可以将DElEXPIRETYPE等命令也称为多态命令,因为无论输入的key 是什么key ,这些命令都可以正确的执行。

    DELEXPIRE等命令和LLEN等命令的区别在于:

    1. 前者是基于类型的多态——一个命令可以同时用于处理多种不同类型的key ;
    2. 后者是基于编码的多态——一个命令可以同时用于处理多种不同编码。

    8.8 内存回收

    因为C语言并不具备自动内存回收功能,所以Redis在自己的对象系统中构建了一个引用计数(refrence counting)技术实现的内存回收机制,通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。

    每隔对象的引用计数信息有redisObject结构的refcount属性记录:

    typedef struct redisObject {
    
    	// ...
    
    	// 引用计数
    	int refcount;
    	
    	// ...
    
    }
    

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

    • 在创建一个新对象时,引用计数的值会被初始化为1;
    • 当对象被一个新程序使用时,他的引用计数值会被增1;
    • 当对象不再被一个程序使用时,他的引用计数会减1;
    • 当对象的引用计数值变为0时,对象所占用的内存会被释放。

    8.9 对象共享

    除了用于实现对象引用计数内存回收机制之外,对象的引用计数属性还带有对象共享的作用。

    假设key A创建了一个包含整数值100的字符串对象作为value object。

    如果这时key B也要创建一个同样保存了整数值100的字符串对象作为value object,那么服务器会有以下两种做法:

    1. 为key B新创建一个包含了整数值100的字符串对象。
    2. 让key A和key B共享同一个字符串对象。

    以上两种方法很明显是第二种更节约内存。

    在Redis中,让多个key 共享同一个value object需要执行一下两个步骤:

    1. 将db key 的value pointer 指向一个现有的value object;
    2. 将被共享的value object 的引用计数加一。

    目前Redis会在初始化server时,创建1w个字符串object,这些object包含了从0到9999的所有integer,当server需要用到值为0-9999的字符串object时,server就会使用这些共享object。

    创建共享的字符串object的数量可以通过修改redis.h/REDIS_SHARED_INTEGERS常量来修改。

    另外,这些共享object不单单只有字符串key可以使用,那些在数据结构中钱逃了字符串对象的对象(linkedlisthashtablezset)都可以使用这些共享对象。

    为什么Redis不共享包含字符串的object?

    当服务器考虑将一个共享object设置为key的value object时,程序需要先检查给定的共享object和key想创建的目标object是否完全相同,只有在共享object和目标object完全相同的情况下,程序才会将共享object作为key的value object,而一个共享object的value越复杂, 验证的复杂度就会越高,消耗CPU的时间也会越多:

    • 如果共享对象保存的是int value,那么验证的操作复杂度是O(1);
    • 如果共享object保存的是string value ,那么验证的操作复杂度是O(N);
    • 如果共享对象是包含了多个value,比如list、hash等,那么复杂度将会是O(N^2);

    因此,尽管共享更复杂的object可以节约更多的内存,但受到CPU时间的限制,Redis只对包含int value的字符串对象进行共享。

    8.10 对象的空转时间

    除了前面介绍过的typeencodingptrrefcount四个属性之外,redisObject结构包含的最后一个属性就是lru属性,该属性记录了对象最后一次被命令程序访问的时间:

    typedef struct redisObject {
    
    	// ...
    	
    	unsigned lru:22;
    	
    	// ...
    
    }
    

    OBJECT IDLETIME命令可以打印出给定key的空转时长,这一空转时长就是通过将当前的时间减去key的value object 的lru time计算得出的。

    OBJECT IDLETIME命令的实现是特殊的,这个命令在访问key 的value object时,不会修改对象的lru属性。

    除此之外,key的空转时长还有另外一个作用,如果server打开了maxmemory选项,并且server用于回收内存的算法为volatile-lru或者allkeys-lru,那么当server占用的memory超过了maxmemory选项所设置的上限值时,空转时长较高的那部分key会优先被server释放,从而回收内存。

  • 相关阅读:
    oracle 触发器的编写
    单例类与常见双下方法
    实现高效率的冒泡排序
    面向对象基础(五)
    面向对象基础(四)
    面向对象基础(三)
    面向对象基础(二)
    面向对象(基础)
    四指针法
    因数法
  • 原文地址:https://www.cnblogs.com/wangxin201492/p/4756461.html
Copyright © 2011-2022 走看看