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释放,从而回收内存。

  • 相关阅读:
    Java实现 计蒜客 拯救行动
    Java实现 计蒜客 拯救行动
    Java实现 LeetCode 174 地下城游戏
    Java实现 LeetCode 174 地下城游戏
    Java实现 LeetCode 174 地下城游戏
    Java实现 LeetCode 173 二叉搜索树迭代器
    Java实现 LeetCode 173 二叉搜索树迭代器
    Visual Studio的SDK配置
    怎样使用CMenu类
    mfc menu用法一
  • 原文地址:https://www.cnblogs.com/wangxin201492/p/4756461.html
Copyright © 2011-2022 走看看