• redis学习笔记——数据类型


    对象处理机制

    Redis 构建了自己的类型系统,这个系统的主要功能包括:
    • redisObject 对象。
    • 基于redisObject 对象的类型检查。
    • 基于redisObject 对象的显式多态函数。
    • 对redisObject 进行分配、共享和销毁的机制。

    (redisObject 实际上是只是一个结构类型。)

    redisObject 的定义位于redis.h :
    /*
    * Redis 对象
    */
    typedef struct redisObject {
    // 类型
    unsigned type:4;
    // 对齐位
    unsigned notused:2;
    // 编码方式
    unsigned encoding:4;
    // LRU 时间(相对于server.lruclock)
    unsigned lru:22;
    // 引用计数
    int refcount;
    // 指向对象的值
    void *ptr;
    } robj;

    type 、encoding 和ptr 是最重要的三个属性。

    type 记录了对象所保存的值的类型,它的值可能是以下常量的其中一个(定义位于redis.h):

    /*
    * 对象类型
    */
    #define REDIS_STRING 0 // 字符串
    #define REDIS_LIST 1 // 列表
    #define REDIS_SET 2 // 集合
    #define REDIS_ZSET 3 // 有序集
    #define REDIS_HASH 4 // 哈希表

    encoding 记录了对象所保存的值的编码,它的值可能是以下常量的其中一个(定义位于redis.h):

    /*
    * 对象编码
    */
    #define REDIS_ENCODING_RAW 0 // 编码为字符串
    #define REDIS_ENCODING_INT 1 // 编码为整数
    #define REDIS_ENCODING_HT 2 // 编码为哈希表
    #define REDIS_ENCODING_ZIPMAP 3 // 编码为zipmap
    #define REDIS_ENCODING_LINKEDLIST 4 // 编码为双端链表
    #define REDIS_ENCODING_ZIPLIST 5 // 编码为压缩列表
    #define REDIS_ENCODING_INTSET 6 // 编码为整数集合
    #define REDIS_ENCODING_SKIPLIST 7 // 编码为跳跃表

    ptr 是一个指针,指向实际保存值的数据结构,这个数据结构由type 属性和encoding 属性决定。
    举个例子, 如果一个redisObject 的type 属性为REDIS_LIST , encoding 属性为REDIS_ENCODING_LINKEDLIST ,那么这个对象就是一个Redis 列表,它的值保存在一个双端链表内,而ptr 指针就指向这个双端链表;

    命令的类型检查和多态

    当执行一个处理数据类型的命令时,Redis 执行以下步骤:
    1. 根据给定key ,在数据库字典中查找和它像对应的redisObject ,如果没找到,就返回NULL 。
    2. 检查redisObject 的type 属性和执行命令所需的类型是否相符,如果不相符,返回类型错误。
    3. 根据redisObject 的encoding 属性所指定的编码,选择合适的操作函数来处理底层的数据结构。
    4. 返回数据结构的操作结果作为命令的返回值。

    对象共享

    有一些对象在Redis 中非常常见,比如命令的返回值OK 、ERROR 、WRONGTYPE 等字符,另外,一些小范围的整数,比如个位、十位、百位的整数都非常常见。

    为了利用这种常见情况,Redis 在内部使用了一个Flyweight 模式:通过预分配一些常见的值对象,并在多个数据结构之间共享这些对象,程序避免了重复分配的麻烦,也节约了一些CPU时间。

    Redis 预分配的值对象有如下这些:
    • 各种命令的返回值,比如执行成功时返回的OK ,执行错误时返回的ERROR ,类型错误时返回的RONGTYPE ,命令入队事务时返回的QUEUED ,等等。
    • 包括0 在内, 小于redis.h/REDIS_SHARED_INTEGERS 的所有整数(REDIS_SHARED_INTEGERS 的默认值为10000)

    Note: 共享对象只能被带指针的数据结构使用。
    需要提醒的一点是,共享对象只能被字典和双端链表这类能带有指针的数据结构使用。

    像整数集合和压缩列表这些只能保存字符串、整数等字面值的内存数据结构,就不能使用共享
    对象。

    引用计数以及对象的销毁

    Redis 的对象系统使用了引用计数技术来负责维持和销毁对象,它的
    运作机制如下:
    • 每个redisObject 结构都带有一个refcount 属性,指示这个对象被引用了多少次。
    • 当新创建一个对象时,它的refcount 属性被设置为1 。
    • 当对一个对象进行共享时,Redis 将这个对象的refcount 增一。
    • 当使用完一个对象之后,或者取消对共享对象的引用之后,程序将对象的refcount 减一。
    • 当对象的refcount 降至0 时,这个redisObject 结构,以及它所引用的数据结构的内存,都会被释放。

    字符串

    字符串编码

    字符串类型分别使用REDIS_ENCODING_INT 和REDIS_ENCODING_RAW 两种编码:
    • REDIS_ENCODING_INT 使用long 类型来保存long 类型值。
    • REDIS_ENCODING_RAW 则使用sdshdr 结构来保存sds (也即是char* )、long long 、double 和long double 类型值。

    换句话来说,在Redis 中,只有能表示为long 类型的值,才会以整数的形式保存,其他类型的整数、小数和字符串,都是用sdshdr 结构来保存。

    编码的选择

    新创建的字符串默认使用REDIS_ENCODING_RAW 编码,在将字符串作为键或者值保存进数据库时,程序会尝试将字符串转为REDIS_ENCODING_INT 编码。

    哈希表

    REDIS_HASH (哈希表) 是HSET 、HLEN 等命令的操作对象, 它使用REDIS_ENCODING_ZIPLIST 和REDIS_ENCODING_HT 两种编码方式:

    字典编码的哈希表

    当哈希表使用字典编码时,程序将哈希表的键(key)保存为字典的键,将哈希表的值(value)保存为字典的值。

    哈希表所使用的字典的键和值都是字符串对象。下图展示了一个包含三个键值对的哈希表:

    压缩列表编码的哈希表

    当使用REDIS_ENCODING_ZIPLIST 编码哈希表时,程序通过将键和值一同推入压缩列表,从而形成保存哈希表所需的键-值对结构:

    新添加的key-value 对会被添加到压缩列表的表尾。
    当进行查找/删除或更新操作时,程序先定位到键的位置,然后再通过对键的位置来定位值的位置。

    编码的选择

    创建空白哈希表时,程序默认使用REDIS_ENCODING_ZIPLIST 编码,当以下任何一个条件被满足时,程序将编码从切换为REDIS_ENCODING_HT :
    • 哈希表中某个键或某个值的长度大于server.hash_max_ziplist_value (默认值为64)。
    • 压缩列表中的节点数量大于server.hash_max_ziplist_entries (默认值为512 )。

    列表

    REDIS_LIST (列表) 是LPUSH 、LRANGE 等命令的操作对象, 它使用REDIS_ENCODING_ZIPLIST 和REDIS_ENCODING_LINKEDLIST 这两种方式编码:

    编码的选择

    创建新列表时Redis 默认使用REDIS_ENCODING_ZIPLIST 编码,当以下任意一个条件被满足时,列表会被转换成REDIS_ENCODING_LINKEDLIST 编码:
    • 试图往列表新添加一个字符串值, 且这个字符串的长度超过server.list_max_ziplist_value (默认值为64 )。
    • ziplist 包含的节点超过server.list_max_ziplist_entries (默认值为512 )。

    阻塞

    BLPOP 、BRPOP 和BRPOPLPUSH 三个命令都可能造成客户端被阻塞,以下将这些命令统称为列表的阻塞原语。
    阻塞原语并不是一定会造成客户端阻塞:
    • 只有当这些命令被用于空列表时,它们才会阻塞客户端。
    • 如果被处理的列表不为空的话,它们就执行无阻塞版本的LPOP 、RPOP 或RPOPLPUSH命令。

  • 相关阅读:
    Mac上Homebrew的安装
    Nodejs全局/缓存路径配置
    Windows 10文件夹Shirt+鼠标右键出现“在此处打开命令窗口”
    CentOS 7上VNCServer的安装使用
    照葫芦画瓢系列之Java --- eclipse下使用maven创建Struts 2项目
    照葫芦画瓢系列之Java --- Maven的集成和使用
    关于集合常见面试问题
    Linux 性能分析大概步骤
    java中的scanner用法
    分享一个内存溢出的问题
  • 原文地址:https://www.cnblogs.com/lukexwang/p/4630356.html
Copyright © 2020-2023  润新知