Redis读书笔记(二)对象

对象介绍


前面介绍的都是基本数据结构,Redis并没有直接使用这些基本数据结构,而是基于这些数据结构创建了一个对象系统,这个系统包含 字符串对象列表对象哈希对象集合对象有序集合对象这五种类型。
Redis通过引用计数实现了内存回收,同时还实现了对象共享机制。
Redis中的键和值都是对象。 Redis中的每个对象都由一个redisObject结构表示,该结构中和 保存数据有关的3个属性分别是 type属性、 encoding属性和 ptr属性:

typedef struct redisObject {

    // 类型,':'表示占用的bit数
    unsigned type:4;

    // 编码
    unsigned encoding:4;

    // 对象最后一次被访问的时间
    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */

    // 引用计数
    int refcount;

    // 指向实际值的指针
    void *ptr;

} robj;

字符串对象

编码实现

  • int:一个字符串对象保存的是整数,并且这个整数可以用long类型表示,那么就将这个整数保存在ptr中(将void*转换成long),并将字符串对象编码为int。
  • raw:保存的是一个字符串值,并且这个字符串值的长度大于39字节,那么将用一个SDS来保存这个字符串,并将对象编码设置为raw。
  • embstr:保存的是一个字符串值,但长度小于等于39字节,那么字符串对象将使用embstr编码方式。

raw和embstr的区别?
rmbstr编码是专门用于保存短字符串的一种优化编码方式,它只进行一次内存分配和回收,它的StringObject和SDS紧挨。而raw编码单独分配StringObject和SDS,需要两次内存分配和回收

编码转化

int和embstr的条件不满足时,会自动转化为raw编码。此外,对embstr执行任何修改命令时,程序会将对象编码转换成raw,因为embstr没有任何操作函数,它是只读的

常用命令

命令 作用
SET 设置值
GET 获取值
APPEND 将给定字符串追加到本字符串末尾
INCRBYFLOAT 将字符串转化为long double然后加上给定的数字
INCRBY 对整数值进行加法计算,只能用于int编码
DECRBY 对整数值进行减法计算,只能用于int编码
STRLEN 返回字符串的长度
SETRANGE 将字符串特定索引上的值设置为给定的字符
GETRANGE 返回字符串指定索引上的字符

列表对象

编码实现

  • 列表对象可以是ziplist或者linkedlist
  • Ziplist 是为了尽可能地节约内存而设计的特殊编码双端链表。它的所有元素在内存中紧挨在一起,却又不能通过下标访问
  • LinkedList是以链表的形式,每个节点是一个字符串对象字符串对象是Redis五种类型的对象中唯一一种会被其他四种对象嵌套的对象

编码转换

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

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

不能同时满足这两个条件的,就需要使用linkelist编码。上面的两个值都是可以通过配置文件修改的。

常用命令

命令 作用
LPUSH 插入表头
RPUSH 插入表尾
LPOP 从表头返回并删除
RPOP 从表尾返回并删除
LINDEX 返回指定节点保存的元素
LLEN 返回表的长度
LINSERT 将值插入到指定位置
LREM 删除包含了给定元素的节点
LTRIM 删除表中所有不在指定索引范围内的节点
LSET 将指定索引的值更新为指定的值

哈希对象

编码实现

  • 哈希对象可用的编码是ziplist或者hashtable。
  • ziplist方式的哈希对象使用ziplist作为底层数据结构,键和值节点紧挨在一起,插入到压缩列表末尾。
  • hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值对来保存:字典的每个键都是一个字符串对象,对象中保存了键值对的键;字典的每个值都是一个字符串对象,对象中保存了键值对的值。

编码转换

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

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

不能满足这两个条件的哈希对象需要使用hashtable编码。上面两个上限值可以通过配置文件修改。

命令 作用
HSET 插入新的键值对
HGET 查找给定键,并返回它的值
HEXISTS 查找给定元素是否存在
HDEL 删除指定键所在的键值对
HLEN 返回键值对的数目
HGETALL 返回所有的键和值

集合对象
集合对象的编码可以是intset或者hashtable。
intset编码使用整数集合作为底层实现,整数集合类似于一个数组,详细见上面介绍。
hashtable编码使用字典作为底层实现。
当下面两个条件都被满足时,使用intset编码,否则,使用hashtable编码
集合对象保存的所有元素都是整数值
集合对象保存的元素数量不超过512
第二个条件的上限可以通过配置文件修改。当两个条件中任一个不满足时,就会自动执行编码转换操作

常用命令

命令 作用
SADD 添加新元素
SCARD 返回元素数量
SISMEMBER 判断给定的元素是否在集合中
SMEMBERS 返回所有元素
SRANDMEMBER 随机返回集合中的一个元素
SPOP 从列表中随机删除一个值
SREM 删除所有给定的元素

有序集合对象

编码实现

  • 有序集合的编码可以是ziplist或者skiplist。
  • ziplist底层使用压缩列表,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),第二个节点保存元素的分值(Score)。压缩列表内按照分值升序排列

skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表

/*
 * 有序集合
 */
typedef struct zset {

    // 字典,键为成员,值为分值
    // 用于支持 O(1) 复杂度的按成员取分值操作
    dict *dict;

    // 跳跃表,按分值排序成员
    // 用于支持平均复杂度为 O(log N) 的按分值定位成员操作
    // 以及范围操作
    zskiplist *zsl;

} zset;

正如源码注释所说,之所以同时采用两种数据结构来实现,是为了互相弥补对方的不足。zskiplist是为了弥补哈希表不能进行范围查询的缺点,而哈希表将单个元素的查询操作从zskiplist的O(logN)优化到了哈希表的O(1)
虽然同时使用了两种结构,但是所有的元素节点都是共用的,即真正的节点只有一份,但是它被两种方式使用了。因此,同时使用两种数据结构,并没有浪费空间,且同时提高了范围查询和单值查询的效率。

有序集合的每个元素成员都是一个字符串对象,而每个元素的分支都是一个double类型的浮点数。

编码转换

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

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

不能满足任何一个,则使用skiplist编码。以上两个条件的上限值是可以在配置文件里修改的。当两个条件的任何一个不被满足时,程序会自动执行编码转换操作。

常用命令

命令 作用
ZADD 插入元素
ZCARD 返回集合元素的数量
ZCOUNT 统计分值在给定范围内的节点的数量
ZRANGE 返回给定索引范围内的所有元素
ZREVRANGE 倒序返回给定范围内的元素
ZRANK 返回元素的集合中的排名
ZREVRANK 返回元素在集合中的倒序排名
ZREM 删除所有包含了给定成员的跳跃表节点
ZSCORE 返回给定元素的分值
上一篇:TP5.0验证码无法显示的问题


下一篇:各大网站的 Python 爬虫登录汇总!爬虫必会技能之一!