• Redis数据类型


    Redis数据类型
    • 字符串
    • 哈希表
    • 列表
    • 集合
    • 有序集
     
    对象处理机制

    对键能执行的命令各部相同,但有些又是通用的。
     
    Redis 必须让每个键都带有类型信息,使得程序可以检查键的类型,并为它选择合适的处理方式
    因为各类型的底层实现(Redis 称为编码,encoding),各不相同,因此程序必须根据键所采取的编码进行不同的操作。
     
    比如说集合类型就可以由字典和整数集合两种不同的数据结构实现。
    当用户执行ZADD命令时,应该不必关心结合使用的是什么编码,只要能将新元素添加到集合就可以了。
    因此,操作数据类型的命令除了要对键的类型进行检查之外,还需要根据数据类型的不同编码进行多态处理
     
    Redis构建了自己的类型系统
    • redisObject 对象
    • 基于 redisObject 对象的类型检查
    • 基于 redisObject 对象的显式多态函数
    • 对 redisObject 进行分配、共享和销毁的机制
     
    redisObject 数据结构
    redisObject 是 Redis 类型系统的核心,数据库中的每个键、值,以及 Redis 本身处理的参数,都表示为这种数据类型。
     
    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_STRING 0 // 字符串
      • REDIS_LIST 1      // 列表
      • REDIS_SET 2       // 集合
      • REDIS_ZSET 3     // 有序集
      • REDIS_HASH 4    // 哈希表
    • encoding(编码)
      • REDIS_ENCODING_RAW 0             // 编码为字符串
      • REDIS_ENCODING_INT 1               // 编码为整数
      • REDIS_ENCODING_HT 2                // 编码为字典
      • REDIS_ENCODING_ZIPMAP 3        // 编码为 zipmap
      • REDIS_ENCODING_LINKEDLIST 4   // 编码为双端链表
      • REDIS_ENCODING_ZIPLIST 5          // 编码为压缩列表
      • REDIS_ENCODING_INTSET 6          // 编码为整数集合
      • REDIS_ENCODING_SKIPLIST 7        // 编码为跳跃表
    • ptr
      • 指向实际保存值得数据结构
        • 这个数据结构由type属性 和encoding 属性决定
      • 例子
        • redisObject 的 type属性为REDIS_LIST,encoding属性为REDIS_ENCODING_LINKEDLIST
        • 那么这个对象就是一个Redis列表,它的值保存在一个双端链表内,而ptr指针就指向这个双端链表
     
     
    命令的类型检查和多态
    当执行一个处理数据类型的命令时,Redis执行以下步骤:
    • 根据给定 key ,在数据库字典中查找和它像对应的 redisObject ,如果没找到,就返回NULL 。
    • 检查 redisObject 的 type 属性和执行命令所需的类型是否相符,如果不相符,返回类型错误。
    • 根据 redisObject 的 encoding 属性所指定的编码,选择合适的操作函数来处理底层的数据结构。
    • 返回数据结构的操作结果作为命令的返回值。
     
    对键key执行LPOP命令的完整过程:
     
    对象共享
    Redis 在内部使用了一个 Flyweight 模式 :通过预分配一些常见的值对象,
    并在多个数据结构之间共享这些对象,程序避免了重复分配的麻烦,也节约了一些CPU时间。
     
    Redis 预分配的值对象有如下这些:
    • 各种命令的返回值,比如执行成功时返回的 OK ,执行错误时返回的 ERROR ,类型错误时返回的 WRONGTYPE ,命令入队事务时返回的 QUEUED ,等等。
    • 包括0在内,小于redis.h/REDIS_SHARED_INTEGERS的所有整数
      • (REDIS_SHARED_INTEGERS的默认值为10000)
    共享对象只能被字典和双端链表这类能带有指针的数据结构使用。
    像整数集合和压缩列表这些只能保存字符串、整数等字面值的内存数据结构,就不能使用共享对象。
     
    引用计数以及对象的销毁
    C语言本身没有自动释放内存的相关机制
    以及对象被引用了多少次?
     
    Redis使用引用计数来负责维持和销毁对象
    • 每个redisObject 结构都带有一个 refcount 属性,指示这个对象被引用了多少次。
    • 当新创建一个对象时,它的 refcount 属性被设置为 1 。
    • 当对一个对象进行共享时,Redis 将这个对象的 refcount 增一。
    • 当使用完一个对象之后,或者取消对共享对象的引用之后,程序将对象的 refcount 减一。
    • 当对象的 refcount 降至 0 时,这个 redisObject 结构,以及它所引用的数据结构的内存,都会被释放。
     
    小结
    • Redis 使用自己实现的对象机制来实现类型判断、命令多态和基于引用计数的垃圾回收。
    • 一种 Redis 类型的键可以有多种底层实现。
    • Redis 会预分配一些常用的数据对象,并通过共享这些对象来减少内存占用,和避免频繁地为小对象分配内存。
     
    字符串

    REDIS_STRING (字符串)是 Redis 使用得最为广泛的数据类型,它除了是 SET 、 GET 等命令的操作对象之外,
    数据库中的所有键,以及执行命令时提供给Redis的参数,都是用这种类型保存的。
     
    字符串类型分别使用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即使用sdshdr保存数据
     
    哈希表

    当哈希表使用字典编码时,程序将哈希表的键( key)保存为字典的键,将哈希表的值( value)保存为字典的值。
    哈希表所使用的字典的键和值都是字符串对象。
    包含三个键值对的哈希表:
     
    默认使用ziplist,压缩列表作为哈希表的编码
     
    列表

    REDIS_LIST(列 表)是LPUSH ,LRANGE 等命令的操作对象
    阻塞的条件
    BLPOP,BRPOP,BRPOPLPUSH 
    三个命令都可能造成客户端被阻塞,将这些命令统称为列表的阻塞原语
     
    阻塞原语并不是一定造成客户端阻塞:
    • 只有当这些命令被用于空列表时,它们才会阻塞客户端
    • 如果被处理的列表不为空的话,它们就执行无阻塞版本的 LPOP 、 RPOP 或 RPOPLPUSH 命令。
     
    BLPOP决定是否对客户端进行阻塞过程:
     
    阻塞
    当一个阻塞原语的处理目标为空键时,执行该阻塞原语的客户端就会被阻塞
    阻塞一个客户端需要执行以下步骤:
    1. 将客户端的状态设为“正在阻塞” ,并记录阻塞这个客户端的各个键,以及阻塞的最长时限( timeout)等数据。
    2. 将客户端的信息记录到 server.db[i]->blocking_keys 中(其中 i 为客户端所使用的数据库号码)。
    3. 继续维持客户端和服务器之间的网络连接,但不再向客户端传送任何信息,造成客户端阻塞。
     
    解除阻塞
    server.db[i]->blocking_keys 是一个字典,字典的键是那些造成客户端阻塞的键,
    而字典的值是一个链表,链表里保存了所有因为这个键而被阻塞的客户端(被同一个键所阻塞的客户端可能不止一个)
     
    当客户端被阻塞之后,脱离阻塞状态有以下三种方法:
    1. 被动脱离:有其他客户端为造成阻塞的键推入了新元素。
    2. 主动脱离:到达执行阻塞原语时设定的最大阻塞时间。
    3. 强制脱离:客户端强制终止和服务器的连接,或者服务器停机
     
    lpush rpush linsert 
    推入新元素
    内部均由pushGenericCommand 去做
    pushGenericCommand 函数执行以下两件事:
    • 检查这个键是否存在于前面提到的 server.db[i]->blocking_keys 字典里,
      • 如果是的话,那么说明有至少一个客户端因为这个 key 而被阻塞,
      • 程序会为这个键创建一个redis.h/readyList 结构,并将它添加到 server.ready_keys链表中。
      • 即将readylist 添加到服务器
    • 将给定的值添加到列表键中。
     
    虽然key3已经不再是空键,但到目前为止,被key3阻塞的客户端还没有任何一个被解除阻塞状态。
    调用handleClientsBlockedOnLists,执行:
    def handleClientsBlockedOnLists():
     
    # 执行直到 ready_keys 为空
    while server.ready_keys != NULL:
     
         # 弹出链表中的第一个 readyList
         rl = server.ready_keys.pop_first_node()
        
         # 遍历所有因为这个键而被阻塞的客户端
         for client in all_client_blocking_by_key(rl.key, rl.db):
     
              # 只要还有客户端被这个键阻塞,就一直从键中弹出元素
              # 如果被阻塞客户端执行的是 BLPOP ,那么对键执行 LPOP
              # 如果执行的是 BRPOP ,那么对键执行 RPOP
              element = rl.key.pop_element()
     
              if element == NULL:
                   # 键为空,跳出 for 循环
                   # 余下的未解除阻塞的客户端只能等待下次新元素的进入了
                   break
              else:
                   # 清除客户端的阻塞信息
                   server.blocking_keys.remove_blocking_info(client)
                   # 将元素返回给客户端,脱离阻塞状态
                   client.reply_list_item(element)
    
    先阻塞先服务FBFS策略,这点从上面伪代码也是可以看出的,根据从前开始取列表
     
    阻塞因超时而取消
    每次Redis服务器常规操作函数( server cron job)执行时,程序都会检查所有连接到服务器
    的客户端查看那些处于"正在阻塞"状态的客户端的最大阻塞时限是否已经过期,
     
    集合

     
    REDIS_SET 集合是SADD,SRANDMEMBER等命令的操作对象
     
     
    第一个添加到集合的元素,决定了创建集合时所使用的编码
    • 如果第一个元素可以表示为 long long 类型值(也即是,它是一个整数),那么集合的初始编码为 REDIS_ENCODING_INTSET
    • 否则,集合的初始编码为 REDIS_ENCODING_HT
     
     
    sinter,sinterstore,求并交集
    sdiff,sdiffstore ,求集合差算法
     
    有序集

    REDIS_ZSET (有 序 集)是 ZADD ,ZCOUNT 等命令的操作对象
     
  • 相关阅读:
    PDO事务处理不能保持一致性
    Android开发中的SQLite事务处理
    Mysql安装
    IIS下https配置及安全整改
    exchang2010OWA主界面添加修改密码选项
    查阅文件技巧
    RHEL yum
    CentOS之——CentOS7安装iptables防火墙
    Linux修改主机名称
    Vmware虚拟机设置静态IP地址
  • 原文地址:https://www.cnblogs.com/Aiapple/p/7248656.html
Copyright © 2020-2023  润新知