美文网首页
Redis 对象

Redis 对象

作者: wayyyy | 来源:发表于2022-09-28 00:07 被阅读0次

    前面已经介绍了 SDS,双端链表,字典,整数集合,压缩列表等数据结构,但是Redis 并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构,创建了一个对象系统:

    • 字符串对象
    • 列表对象
    • 哈希对象
    • 集合对象
    • 有序集合对象

    redis 使用这些对象来表示数据库中的键和值。

    typedef struct redisObject {
        unsigned type;
        unsigned encoding;
        void* ptr;  
        int refcount;    //  引用计数
    }
    
    object.png

    类型

    对于redis保存的键值对来说:

    • 键总是一个字符串对象
    • 值可以是字符串对象,列表对象,哈希对象,集合对象,有序集合对象

    对象的type属性记录了对象的类型。

    redis > SET msg "hello world"
    OK
    redis > TYPE msg
    string
    

    编码

    对象的ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。
    使用OBJECT ENCODING命令可以查看一个数据库键的值对象的编码

    redis> SET msg "hello world"
    OK
    redis> OBJECT ENCODING msg
    "emstr"
    
    type encoding 对象
    REDIS_STRING REDIS_ENCODING_INT 使用整数值实现的字符串对象
    REDIS_STRING REDIS_ENCODING_EMBSTR 使用embstr编码的简单动态字符串实现的字符串对象
    REDIS_STRING REDIS_ENCODING_RAW 使用简单动态字符串实现的字符串对象
    REDIS_LIST REDIS_ENCODING_ZIPLIST 使用压缩列表实现的列表对象
    REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用双端链表实现的列表对象
    REDIS_HASH

    类型检查与命令多态

    Redis 中用于操作键的命令基本上可以分为2类:

    • 其中一种对任何类型的都可以执行
    • 而另一种只能对特定类型的键执行
    类型检查

    类型特定命令进行的类型检查是通过来实现,在执行一个类型特定命令之前,服务器会先检查输入数据库键的值对象是否为执行命令所需的类型。

    多态命令

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

    比如列表对象有ziplistlinkedlist2种编码可以使用,比如现在可以对一个键执行LLEN命令,

    • 如果列表对象的编码为ziplist,那么说明列表对象的实现为压缩列表,程序将使用ziplistLen函数来返回列表的长度
    • 如果列表对象的编码为linkedlist,那么说明列表对象的实现为双端列表,程序将使用listLength函数来返回双端链表的长度

    完整实现过程如图:

    LLEN key.png

    内存回收

    C语言并不具备内存自动回收功能,所以Redis在自己的对象系统中构建了一个引用计数实现的内存回收机制。

    typedef struct redisObject {
        // ...
        int refcount;    //  引用计数
    } robj;
    
    • 对象共享
      除了用于实现引用计数内存回收机制外,对象的引用计数属性还带有对象共享的作用。
      比如键A创建了一个包含整数值100的字符串对象作为值对象。此时键B也要创建一个包含整数值100的字符串对象作为值对象。
      那么可以:

      1. 将数据库键的值指针指向一个现有的值对象
      2. 将被共享的值的对象的引用计数增1

      实际上,Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值。

      void initServer()
      {
          ...
          // 创建共享对象
          createSharedObjects();
          ...
      }
      
      #define REDIS_SHARED_INTEGERS 10000
      void createSharedObjects()
      {
          ...
          // 常用整数
          for (j = 0; j < REDIS_SHARED_INTEGERS; j++) {
              shared.integers[j] = createObject(REDIS_STRING,(void*)(long)j);
              shared.integers[j]->encoding = REDIS_ENCODING_INT;
          }
          ...
      }
      
      redis> SET A 100
      OK
      redis> OBJECT REFCOUNT A
      (integer) 2
      redis> SET B 100
      OK
      redis> OBJECT REFCOUNT A
      (integer) 3
      redis> OBJECT REFCOUNT B
      (integer) 3
      

    对象空转时长

    typedef struct redisObject {
        // ...
        unsigned lru:22;    //  引用计数
    } robj;
    

    lru记录了对象最后一次被命令程序访问的时间。使用OBJECT IDLETIME可以打印出给定键的空转时长,这一空转时长就是通过当前时间 - lru时间。

    redis > SET msg "hello world"
    OK
    
    # 等待一段时间
    redis> OBJECT IDLETIME msg
    (integer) 20
    
    # 访问
    
    # redis> OBJECT IDLETIME msg
    (integer) 0
    

    键的空转时就是:

    • 如果服务器打开了maxmemory选项
    • 并且服务器回收内存的算法为``
      那么当服务器占用内存数超过了所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存。

    相关文章

      网友评论

          本文标题:Redis 对象

          本文链接:https://www.haomeiwen.com/subject/vzcwwhtx.html