【Redis】对象

概述

  • 对象包含: 字符串对象, 列表对象, 哈希对象, 集合对象, 有序集合对象
  • Redis 用基于数据结构实现的对象系统来实现数据库, 而没有直接用数据结构
  • 对于同一种对象, 可以在不同的使用场景下使用不同的数据结构来实现, 从而优化使用效率
  • Redis通过引用计数实现内存回收机制对象共享技术
  • Redis通过对象的访问时间属性计算空转时间, 当内存不够时根据空转时间对内存进行回收。
  • 在Redis数据库中每当我们创建一个键值对时, 都会创建至少创建两个对象,一个对象用作键, 一个对象用作值
  • 键总是字符串对象, 而值可以是五大对象中的一种, 因此称一个键为"XX键"时, 指这个数据库键的值是XX对象, 例如哈希键, 字符串键等等.

对象数据结构

typedef struct redisObject {

    // 类型
    unsigned type:4;

    // 编码
    unsigned encoding:4;

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

    // 引用计数
    int refcount;

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

} robj;

type 类型

type属性记录了对象的类型

Redis 5大对象

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

redis.h 头文件中定义的对象常量的值

/* Object types */
// 对象类型
#define REDIS_STRING 0 
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4

调用redis中的 TYPE 命令可以返回对象类型

encoding 编码

encoding属性记录了对象的底层实现数据结构

redis.h 头文件中定义可能的值

#define REDIS_ENCODING_RAW 0     /* 简单动态字符串 */
#define REDIS_ENCODING_INT 1     /* long类型的整数 */
#define REDIS_ENCODING_HT 2      /* 字典 */
#define REDIS_ENCODING_ZIPMAP 3  /* 压缩字典 */
#define REDIS_ENCODING_LINKEDLIST 4 /* 双向链表 */
#define REDIS_ENCODING_ZIPLIST 5 /* 压缩列表 */
#define REDIS_ENCODING_INTSET 6  /* 整数集合 */
#define REDIS_ENCODING_SKIPLIST 7  /* 跳跃列表 */
#define REDIS_ENCODING_EMBSTR 8  /* embstr */

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

根据不同的使用场景, 为不同的对象设置不同的编码(数据结构), 可以优化对象的效率

字符串对象

字符串对象的编码可以是: int , raw(SDS简单动态字符串) 和 embstr。

编码转换

使用int编码:

  • 字符串保存的是整数值
  • 可以用long类型表示

直接使用整数值保存在字符串对象结构的ptr属性里(将void* 转换成 long类型)
并且将字符串编码设置为REDIS_ENCODING_INT

在这里插入图片描述
使用raw编码:

  • 保存的是字符串
  • 字符串的长度大于39字节

在这里插入图片描述

使用embstr编码:

  • 保存的是字符串
  • 字符串值的长度小于39字节
  • embstr 一旦被执行任何修改命令, 会转换为 raw, 因为 Redis没有问 embstr 提供任何的修改程序。

在这里插入图片描述

embstr和sds的区别

embstr将redisObject对象结构和sdshdr结构通存放在连续的内存空间中, 只需要进行一次内存分配

优点

  • 内存分配和释放都只需要调用一次相关函数
  • 连续的内存存放对象和编码更好地利用缓存带来的优势

列表对象

列表对象的编码可以是ziplist和linkedlist

使用ziplist编码的列表对象

在这里插入图片描述

使用linkedlist编码的列表对象

在这里插入图片描述

注: linkedlist里存储的是字符串对象, 字符串对象是Redis五种对象中唯一一种会被其他四种对象嵌套的对象

编码转换

使用ziplist :

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

不能满足上述条件的列表对象使用linkedlist编码

哈希对象

哈希对象的编码可以是 ziplisthashtable

ziplist编码的哈希对象使用压缩列表作为底层

  • ziplist的哈希表中, 同一键值对的两个节点总是紧挨在一起, 保存键的节点在前, 保存值的节点在后.
  • 先添加到哈希对象中的兼职对会放在压缩列表的表头方向

在这里插入图片描述
在这里插入图片描述

hashtable编码的哈希对象使用字典作为底层实现,

  • 字典的都是一个字符串对象, 保存了键值对的
  • 字段的都是一个字符串对象, 保存了键值对的

在这里插入图片描述

编码转换

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

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

不满足上述条件的哈希对象使用hashtable编码

集合对象

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

intset使用整数集合作为底层实现
在这里插入图片描述

hsahtable底层使用字段实现, 每一个键都是字符串对象, 保存着集合的元素, 字典每个键的值都为NULL
在这里插入图片描述

编码转换

使用intset编码

  • 所有元素都是整数值
  • 元素数量不超过512个

不满足上述2个条件的集合对象需使用hashtable编码

有序集合对象

有序集合对象编码可以是ziplist或者skiplist
有序集合的每个元素成员都是一个字符串对象, 每个元素的分值都是一个double类型.
ziplist编码使用压缩列表, 每个集合元素使用两个紧挨在一起的压缩列表节点来保存, 第一个节点保存元素的成员, 第二个节点保存元素的分值, 压缩列表集合内的元素按分值从小到大排序

在这里插入图片描述
在这里插入图片描述

如果对象使用skiplist编码, 则会使用zset结构作为底层实现

typedef struct zset {

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

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

} zset;

使用zskiplist跳跃表对集合进行范围型操作
使用dict字典堆可以时间复杂度O(1)对成员的分值进行查找操作
二者会通过共享指针来共享相同元素的成员和分值, 因此并不会产生任何重复的成员或分值

在这里插入图片描述

在这里插入图片描述

编码转换

使用ziplist

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

不满足上述条件的有序集合对象使用skiplist编码

类型检查与命令多态

Redis中的命令基本可以分为两类:

  • 可以对任何类型执行的键执行, DEL命令, EXPIRE命令, RENAME命令, TYPE命令等,
  • 只能对特定类型执行的命令, 如SET, GET, APPEND等只能对字符串执行

类型检查:

在执行一个命令之前, Redis 会通过检查 redisObject 结构的 type 属性对象是否符合
如果对执行的命令与键不符, Redis会返回一个类型错误

多态命令:

对与同一命令, Redis会根据同一对象不同的编码来选择正确的命令实现, 借用面向对象的术语来说, 可以认为该命令是多态的

例如对同一LLEN命令

  • 如果对象的编码为ziplis, 那么说明对象的实现为压缩列表, 程序将用ziplistLen函数来返回列表的长度
  • 如果独享的编码为linkedlist, 那么程序将使用listLength函数来返回双向链表的长度

内存回收

C语言不具备内存回收功能, 因此Redis使用引用计数法来实现内存回收机制

每个对象的引用计数信息由 redisObject 结构的 refcount 属性记录

  • 创建一个新对象时, refcount = 1
  • 当一个对象被新程序使用时, refcount ++;
  • 当一个对象不再被程序使用时, refcount --;
  • 当对象的引用计数器值变为 0 时, 对象所占的内存会被回收

对象共享

Redis 利用引用计数属性实现对象共享

如果键A创建了一个保存100整数的字符串对象, 那么当键B也需要使用一个100整数的字符串对象时, 显然, 键A和键B共享一个对象比较节约内存

实现对象共享步骤

  • 将数据库键的值指向一个现有的值对象
  • 让该对象的引用计数增一

在这里插入图片描述

共享对象时, 要首判断给定的共享对象和目标对象是否相等

  • 如果共享对象是保存整数的字符串, 那么该验证操作的时间复杂度为O(1)
  • 如果共享对象 是保存字符串值的字符串, 那么该验证操作的时间复杂度为O(N)
  • 如果共享对象 是保存了多个值或对象的对象, 那么该验证操作的时间复杂度为O(N^2)

因此为了避免验证对于CPU的消耗
redis仅对包含整数值的字符串对象进行共享

对象的空转时长

redisObject中的 lru属性记录了对象最后一次被命令程序访问的时间

OBJECT IDLETIME命令可以打印出给定键的空转时长, 该命令是通过lru时间计算出的

redis> SET msg "hello world"
redis> OBJECT IDLETIME msg
redis> 20

# 等一会

redis> OBJECT IDLETIME msg
redis> 350

# 访问一次msg
redis> GET msg
redis> "hello world"
redis> OBJECT IDLETIME msg
redis> 0

操作AIP

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考资料: 《Redis设计与实现》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值