Redis-数据结构与对象-对象

对象

Redis并没有直接使用这些数据结构来实现键值对数据库, 而是基于这些数据结构创建了一个对象系统, 这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型。

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

 当我们对一个数据库键执行 TYPE 命令时, 命令返回的结果为数据库键对应的值对象的类型, 而不是键对象的类型。

对象类型根据使用场景的不同有多种编码方式,对应不同的底层实现方式。对象的修改可能发生编码转换。

编码对应的底层数据结构:


字符串

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

如果一个字符串对象保存的是整数值, 并且这个整数值可以用 long 类型来表示, 那么字符串对象会将整数值保存在字符串对象结构的 ptr属性里面(将 void* 转换成 long ), 并将字符串对象的编码设置为 int 。

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

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

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

set [key] [value]新建或更新

get [key]

del [key1] [key2]

mest [key1] [value1] [key2] [value2]一次添加多个键值对

mget [key1] [key2]一次查询多个键值对


列表

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

lpush [key] [value1] [value2]从列表头插入一个或多个元素

rpush [key] [value1] [value2]从列表尾插入一个或多个元素

lpop [key] [count]移除并返回列表头部的若干元素

rpop [key] [count]移除并返回列表尾部的若干元素

lrange [key] [indexStart] [indexEnd]返回列表指定区间内的元素,0代表第一个,-1代表倒数第一个

llen [key]返回列表的长度。如果列表不存在,则key被解释为空列表,返回0


哈希

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

ziplist 编码的哈希对象使用压缩列表作为底层实现, 每当有新的键值对要加入到哈希对象时, 程序会先将保存了键的压缩列表节点推入到压缩列表表尾, 然后再将保存了值的压缩列表节点推入到压缩列表表尾, 因此:

  • 保存了同一键值对的两个节点总是紧挨在一起, 保存键的节点在前, 保存值的节点在后;
  • 先添加到哈希对象中的键值对会被放在压缩列表的表头方向, 而后来添加到哈希对象中的键值对会被放在压缩列表的表尾方向。

hashtable 编码的哈希对象使用字典作为底层实现, 哈希对象中的每个键值对都使用一个字典键值对来保存:

  • 字典的每个键都是一个字符串对象, 对象中保存了键值对的键;
  • 字典的每个值都是一个字符串对象, 对象中保存了键值对的值。

**********************************************************************

hset [key] [field] [value]

hget [key] [field]

hgetall [key]

hdel [key] [field1] [field2]

hmset [key] [field1] [value1] [field2] [value2]一次设置多个field

hmget [key] [field1] [field2]一次查询多个field

hlen [key]查询一个hash有几个field

hexists [key] [field]查询一个hash是否存在某个字段


集合

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

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

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

set元素不能重复

sadd [key] [member1] [member2]

smembers [key]

sismember [key] [member]判断是否成员

srem [key] [member1] [member2]删除指定成员


有序集合

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

ziplist 编码的有序集合对象使用压缩列表作为底层实现, 每个集合元素使用两个紧挨在一起的压缩列表节点来保存, 第一个节点保存元素的成员(member), 而第二个元素则保存元素的分值(score)。压缩列表内的集合元素按分值从小到大进行排序, 分值较小的元素被放置在靠近表头的方向, 而分值较大的元素则被放置在靠近表尾的方向。

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

typedef struct zset {
    zskiplist *zsl;
    dict *dict;
} zset;

这两种数据结构通过指针来共享相同元素的成员和分值,使元素既不重复又能保持有序。


Redis的命令有的可以处理多种对象类型,有的可以处理一种对象的多种编码,体现出一定的多态性。

Redis在对象系统中使用引用计数来实现内存回收。

引用计数还带来对象共享的功能,考虑到对象检查的复杂度,Redis 只对包含整数值的字符串对象进行共享。

redisObject 结构包含的最后一个属性为 lru 属性, 该属性记录了对象最后一次被命令程序访问的时间,OBJECT IDLETIME 命令可以打印出给定键的空转时长, 这一空转时长就是通过将当前时间减去键的值对象的 lru 时间计算得出的。如果服务器打开了 maxmemory 选项, 并且服务器用于回收内存的算法为 volatile-lru 或者 allkeys-lru , 那么当服务器占用的内存数超过了 maxmemory 选项所设置的上限值时, 空转时长较高的那部分键会优先被服务器释放, 从而回收内存。

posted @   Saturn5  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示