• Redis 对象


    通过上几节,我们了解到Redis底层的一些重要的数据结构,比如简单动态字符串、链表、字典、跳跃表等等。

    除了这几个还有整数集合,压缩列表。会在本章进行简单的描述。

    而Redis,是基于上述几种数据结构创建了对象,再由对象构建Redis数据库,所以每个对象至少用了一种上述的数据结构。

    这个对象有字符串对象、列表对象、哈希对象、集合对象和有序集合对象。

    Redis 对象的类型与编码

    Redis 使用对象来表示数据库中的键和值, 每次当我们在 Redis 的数据库中新创建一个键值对时, 我们至少会创建两个对象, 一个对象用作键值对的键(键对象), 另一个对象用作键值对的值(值对象)。

    Redis 中的每个对象都由一个 redisObject 结构表示, 该结构中和保存数据有关的三个属性分别是 type 属性、 encoding 属性和 ptr 属性:

    typedef struct redisObject {
    
        // 类型
        unsigned type:4;
    
        // 编码
        unsigned encoding:4;
    
        // 指向底层实现数据结构的指针
        void *ptr;
    
        // ...
    
    } robj;

    类型

    对象的 type 属性记录了对象的类型,如下表格所示:

    类型常量 对象名称
    REDIS_STRING 字符串对象
    REDIS_LIST 列表对象
    REDIS_HASH 哈希对象
    REDIS_SET 集合对象
    REDIS_ZSET 有序集合对象

    对Redis数据库来说,它的键总是字符串对象,而值可以为上表格中任意一种。

    所以当我们用 type 命令获取键的类型时,返回的是键对应值对象的类型:

     编码

    对象的 ptr 指针指向对象的底层实现数据结构, 而这些数据结构由对象的 encoding 属性决定。

    encoding 属性记录了对象所使用的编码, 也即是说这个对象使用了什么数据结构作为对象的底层实现,下图为编码对应底层数据结构表:

    编码常量 编码对应的底层数据结构
    REDIS_ENCODING_INT long 类型的整数
    REDIS_ENCODING_EMBSTR embstr 编码的简单动态字符串
    REDIS_ENCODING_RAW 简单动态字符串
    REDIS_ENCODING_HT 字典
    REDIS_ENCODING_LINKEDLIST 双端链表
    REDIS_ENCODING_ZIPLIST 压缩列表
    REDIS_ENCODING_INTSET 整数集合  
    REDIS_ENCODING_SKIPLIST 跳跃表和字典

    每种类型的对象都至少使用了两种不同的编码, 如下列表列出了每种类型的对象可以使用的编码:

    类型 编码 对象
    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_ENCODING_ZIPLIST 使用压缩列表实现的哈希对象
    REDIS_HASH REDIS_ENCODING_HT 使用字典实现的哈希对象
    REDIS_SET REDIS_ENCODING_INTSET 使用整数集合实现的集合对象
    REDIS_SET REDIS_ENCODING_HT 使用字典实现的集合对象
    REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用压缩列表实现的有序集合对象
    REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳跃表和字典实现的有序集合对象

    使用 OBJECT ENCODING 命令可以查看一个数据库键的值对象的编码:

     字符串对象

    字符串对象的编码可以是 int 、 raw 或者 embstr 。

    如果一个字符串对象保存的是整数值, 并且这个整数值可以用 long 类型来表示, 那么字符串对象的编码为 int 。

    例如:

     如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度大于 39 字节, 那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值, 并将对象的编码设置为 raw 。

    例如:

    如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度小于等于 39 字节, 那么字符串对象将使用 embstr 编码的方式来保存这个字符串值。

    embstr 编码是专门用于保存短字符串的一种优化编码方式, 这种编码和 raw 编码一样, 都使用 redisObject 结构和 sdshdr 结构来表示字符串对象, 但 raw 编码会调用两次内存分配函数来分别创建 redisObject 结构和 sdshdr 结构, 而 embstr 编码则通过调用一次内存分配函数来分配一块连续的空间, 空间中依次包含 redisObject 和 sdshdr 两个结构。如下图结构:

    embstr 编码的优点:embstr 编码将创建字符串对象所需的内存分配、内存释放次数从 raw 编码的两次降低为一次。

    编码转换

    int 编码的字符串对象和 embstr 编码的字符串对象在条件满足的情况下, 会被转换为 raw 编码的字符串对象。

    对于 int 编码的字符串对象来说, 如果我们向对象执行了一些命令, 使得这个对象保存的不再是整数值, 而是一个字符串值, 那么字符串对象的编码将从 int 变为 raw 。

    对于 embstr 编码的字符串对象来说, 因为 Redis 没有为 embstr 编码的字符串对象编写任何相应的修改程序,所以 embstr 编码的字符串对象实际上是只读的。

    当我们对 embstr 编码的字符串对象执行任何修改命令时, 程序会先将对象的编码从 embstr 转换成 raw , 然后再执行修改命令。

    列表对象

    列表对象的编码可以是 ziplist 或者 linkedlist 。

    ziplist 编码的列表对象使用压缩列表作为底层实现, 每个压缩列表节点(entry)保存了一个列表元素。

    如下例子:

     如果 golang键的值对象使用的是 ziplist 编码,则列表对象在内存中的结构:

     如果 golang键的值对象使用的是 linkedlist 编码,则列表对象在内存中的结构:

     编码转换

    当列表对象可以同时满足以下两个条件时, 列表对象使用 ziplist 编码:

    1. 列表对象保存的所有字符串元素的长度都小于 64 字节;
    2. 列表对象保存的元素数量小于 512 个;

    不能满足这两个条件的列表对象需要使用 linkedlist 编码。

    哈希对象

    哈希对象的编码可以是 ziplist 或者 hashtable 。

    如下例子:

     如果 profile 键的值对象使用的是 ziplist 编码,那么哈希对象在内存中的结构为:

     如果 profile 键的值对象使用的是 hashtable 编码,那么哈希对象在内存中的结构为:

     编码转换

    当哈希对象可以同时满足以下两个条件时, 哈希对象使用 ziplist 编码:

    1. 哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;
    2. 哈希对象保存的键值对数量小于 512 个;

    不能满足这两个条件的哈希对象需要使用 hashtable 编码。、

    集合对象

    集合对象的编码可以是 intset 或者 hashtable 。

    intset 编码的集合对象使用整数集合作为底层实现, 集合对象包含的所有元素都被保存在整数集合里面。

    如下面的例子:

     则使用intset编码的集合对像在内存中的结构为:

      hashtable 编码的集合对象使用字典作为底层实现, 字典的每个键都是一个字符串对象, 每个字符串对象包含了一个集合元素, 而字典的值则全部被设置为 NULL 。

    如下图例子:

    则使用 hashtable 编码的集合对像在内存中的结构为:

     编码转换

    当集合对象可以同时满足以下两个条件时, 对象使用 intset 编码:

    1. 集合对象保存的所有元素都是整数值;
    2. 集合对象保存的元素数量不超过 512 个;

    不能满足这两个条件的集合对象需要使用 hashtable 编码。

    有序集合对象

    有序集合的编码可以是 ziplist 或者 skiplist 。

    ziplist 编码的有序集合对象使用压缩列表作为底层实现, 每个集合元素使用两个紧挨在一起的压缩列表节点来保存, 第一个节点保存元素的成员(member), 而第二个元素则保存元素的分值(score)。

    压缩列表内的集合元素按分值从小到大进行排序, 分值较小的元素被放置在靠近表头的方向, 而分值较大的元素则被放置在靠近表尾的方向。

    如下图例子:

     如果 language 键的值对象使用的是 ziplist 编码,那么他在内存中的结构为:

     如果 language 键的值对象使用的是 skiplist 编码,那么他在内存中的结构可参考:

    https://www.cnblogs.com/hulunbao/p/13963881.html

    编码的转换

    当有序集合对象可以同时满足以下两个条件时, 对象使用 ziplist 编码:

    1. 有序集合保存的元素数量小于 128 个;
    2. 有序集合保存的所有元素成员的长度都小于 64 字节;

    不能满足以上两个条件的有序集合对象将使用 skiplist 编码。

    参考文献

    Redis设计与实现第二版

    https://www.cnblogs.com/lizhenghn/p/5322887.html

     
  • 相关阅读:
    转载:iOS开发的22个奇谲巧技
    解决pathForResource返回nil, 无法读取plist文件问题
    小项目三:登陆窗口
    小项目一: UIButton的使用
    转载:iOS 8 自适应 Cell
    转载:iOS 8 AutoLayout与Size Class自悟
    转载:总结iOS 8和Xcode 6的各种坑
    [转]Xcode6中如何添加pch文件
    UIButton和UIImageView的区别
    控件的属性
  • 原文地址:https://www.cnblogs.com/hulunbao/p/14050563.html
Copyright © 2020-2023  润新知