Redis 对象系统

Stella981
• 阅读 643

Redis 为了更好的实现键值对数据库,创建了一个对象系统,以下为 Redis 对象系统的相关知识简介。

redisObject

Redis 使用对象来表示数据库中的键和值,每次在 Redis 数据库中创建一个键值对时,至少会创建两个对象。一个为键对象,一个为值对象。
Redis 对象的定义如下:

typedef struct redisObject {
    unsigned type:4;       /* 对象类型 */
    unsigned encoding:4;   /* 对象编码 */
    unsigned lru:LRU_BITS; /* 对象空转时长 */
    int refcount;          /* 引用计数 */
    void *ptr;             /* 指向底层数据结构的指针 */
} robj;

类型

对象的属性 type 记录了对象是什么类型。这个属性的值的相关定义如下:

#define OBJ_STRING 0
#define OBJ_LIST 1
#define OBJ_SET 2
#define OBJ_ZSET 3
#define OBJ_HASH 4

前面说到,键和值都是一个对象。键总是一个字符串对象,则属性 type 为 OBJ_STRING ,值对象则可以是字符串对象,列表对象等,不同的类型的对象,实则就是属性 type 不同的 redisObject 。如列表键,指的是属性 type 为 OBJ_LIST 的值对象。
可以通过使用命令 TYPE 在 Redis 客户端中获取指定键的数据类型。

127.0.0.1:6379> SET msg "hello redis"
OK
127.0.0.1:6379> TYPE msg
string

编码

对象的属性 encoding ,其实指的是该对象底层所使用的数据结构类型,即指针 ptr 所指向的数据结构,相关的定义如下:

#define OBJ_ENCODING_RAW 0     /* 简单动态字符串 */
#define OBJ_ENCODING_INT 1     /* 整型 */
#define OBJ_ENCODING_HT 2      /* 字典 */
#define OBJ_ENCODING_ZIPMAP 3  /* 压缩字典(不再使用)*/
#define OBJ_ENCODING_LINKEDLIST 4 /* 双端链表(不再使用) */
#define OBJ_ENCODING_ZIPLIST 5 /* 压缩列表 */
#define OBJ_ENCODING_INTSET 6  /* 整数集合 */
#define OBJ_ENCODING_SKIPLIST 7  /* 跳表 */
#define OBJ_ENCODING_EMBSTR 8  /* Embedded 编码的简单动态字符串 */
#define OBJ_ENCODING_QUICKLIST 9 /* 快速列表,基于压缩列表实现的列表,取代了之前的双端链表 */

不同的对象类型,可以使用的对象编码也有所不同,下面为类型与编码的对应关系:

类型

编码

对象

OBJ_STRING

OBJ_ENCODING_INT

使用整数值实现的字符串对象

OBJ_STRING

OBJ_ENCODING_EMBSTR

使用 embstr 编码的简单动态字符串实现的字符串对象

OBJ_STRING

OBJ_ENCODING_RAW

使用简单动态字符串实现的字符串对象

OBJ_LIST

OBJ_ENCODING_QUICKLIST

使用 quicklist 实现的列表对象

OBJ_HASH

OBJ_ENCODING_ZIPLIST

使用压缩列表实现的哈希对象

OBJ_HASH

OBJ_ENCODING_HT

使用字典实现的哈希对象

OBJ_SET

OBJ_ENCODING_INTSET

使用整数集合实现的集合对象

OBJ_SET

OBJ_ENCODING_HT

使用字典实现的集合对象

OBJ_ZSET

OBJ_ENCODING_ZIPLIST

使用压缩列表实现的有序集合对象

OBJ_ZSET

OBJ_ENCODING_SKIPLIST

使用跳跃表实现的有序集合对象

通过命令 OBJECT ENCODING 命令可以在 Redis 客户端中查看指定键的对象编码。

127.0.0.1:6379> LPUSH DATABASE:LIST sqlserver mysql pgsql
(integer) 3
127.0.0.1:6379> OBJECT ENCODING DATABASE:LIST
"quicklist"

空转时间

对象属性 lru 记录的是对象最后一次被访问的时间。通过该属性,可以利用命令 OBJECT IDLETIME 获取对象的空转时长(命令 OBJECT IDLETIME 不会更新 lru 属性)。
OBJECT IDLETIME 计算得出的空转时长是通过当前时间减去指定对象的 lru 属性得出的,其单位为秒。

127.0.0.1:6379> set msg "hello world"
OK
127.0.0.1:6379> OBJECT IDLETIME msg
(integer) 10

属性 lru 除了应用于命令 OBJECT IDLETIME 外,还有另一项作用。
当服务器开启了 maxmemory 选项,并且服务器用于回收内存的算法为 volatile-lru 或者 allkeys-lru 的话,那么当 Redis 实例的占用的内存超过 maxmemory 后,空转时间较长的对象将会优先被回收。

引用计数

redisObject 还有一个属性 refcount ,它所记录的是对象被引用数。Redis 构建的对象系统使用引用计数机制实现内存回收。引用计算为 0 的对象将会被回收。

引用计数由一个特殊值,为 OBJ_SHARED_REFCOUNT ,该值为 INT_MAX 。当一个对象为共享对象时,该对象的 refcount 属性将会被赋予 OBJ_SHARED_REFCOUNT 。
每当需要使用一个对象时,都需要创建一个对象,而如果已经存在了一个一样的对象的话,再创建一个对象就会显得多余,完全可以共享相同的对象。而共享对象就是在 Redis 可以被共享使用的对象,共享对象在 Redis 中被多处使用,但指针所指向的对象都是同一个。
不过在 Redis 中,只有整数值小于 10000 的字符串对象可以被共享,在 Redis 实例启动时,会创建值为 0 到 9999 的类型为 OBJ_STRING ,编码为 OBJ_ENCODING_INT 的字符串对象。该值只可以通过修改源码 server.h 中的 OBJ_SHARED_INTEGERS 常量修改。
通过共享对象需要可以有效减少对象的数量从而减轻内存的压力,但是判断一个对象是否完全相同是需要进行运算,对象构成越复杂,计算的时间复杂度越高,相应的耗费的 CPU 资源就越多。共享对象为整型,计算的时间复杂度为 O(1) ,而共享对象为字符串时,计算复杂度为 O(N) ,换句话说就是时间置换空间,所以尽管共享对象可以节省空间,但是相应的会消耗 CPU 资源,所以 Redis 只针对整型字符串对象进行共享。

编码转换

redisObject 同一种类型可以有多种编码,即多种数据结构的实现。而同一类型的对象是可以进行编码的转换的,即底层数据结构可以进行转换。
控制对象编码转换的配置在配置文件 redis.conf 中(如果使用 redis.conf 作为配置文件启动的话)。

OBJ_STRING 类型的对象编码转换

字符串对象有三种编码,以下为三种编码的使用场景以及编码转换发生条件。

  • OBJ_ENCODING_INT : 当对象所保存的值为 64 位整数值时,使用 OBJ_ENCODING_INT 作为对象的编码。
  • OBJ_ENCODING_EMBSTR : 当对象保存的是一个字符串对象时,且其字节长度小于常量 OBJ_ENCODING_EMBSTR_SIZE_LIMIT 时,对象使用 OBJ_ENCODING_EMBSTR 编码,常量 OBJ_ENCODING_EMBSTR_SIZE_LIMIT 在源码文件 object.c 。embstr 编码是专门用于保存短字符串的一种优化编码方式,通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含 redisObject 和简单动态字符串两个结构,而且因为数据保存在同一块内存中,可以更好的利用缓存。
  • OBJ_ENCODING_RAW : 当对象保存的是一个字符串对象时,且其字节长度大于 OBJ_ENCODING_EMBSTR_SIZE_LIMIT 时,对象使用 OBJ_ENCODING_RAW 编码。虽然 OBJ_ENCODING_RAW 没有 OBJ_ENCODING_EMBSTR 的优点,但是 OBJ_ENCODING_EMBSTR 编码的对象的值是不可变的,所以当字符串发生变化时,如使用命令 APPEND ,编码为 OBJ_ENCODING_EMBSTR 字符串对象将会转换成 OBJ_ENCODING_RAW 编码。

OBJ_HASH 类型的对象编码转换

相关配置项如下:

hash-max-ziplist-entries 512
hash-max-ziplist-value 64

配置的含义为:

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

不能满足上述条件的哈希对象将使用 OBJ_ENCODING_HT 作为对象编码,即使用字典作为底层数据结构。否则使用 OBJ_ENCODING_ZIPLIST 作为对象编码,即压缩列表作为底层数据结构。

OBJ_SET 类型的对象编码转换

相关配置如下:

set-max-intset-entries 512

配置的含义为:

  • 集合对象保存的元素数量不超过512个

不能满足上述条件,或是元素不全部为整数( intset 数据结构的天然限制)的集合对象,将使用 OBJ_ENCODING_HT 作为对象编码,即使用字典作为底层数据结构。否则使用 OBJ_ENCODING_INTSET 作为对象编码,即 intset 作为底层数据结构。

OBJ_ZSET 类型的对象编码转换

相关配置如下:

zset-max-ziplist-entries 128
zset-max-ziplist-value 64

配置含义为:

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

不能满足上述条件的有序集合对象,将使用 OBJ_ENCODING_SKIPLIST 作为对象编码,即跳跃表作为底层数据结构。否则使用 OBJ_ENCODING_ZIPLIST 作为对象编码,即压缩列表作为底层数据结构。

OBJ_LIST 类型的对象编码转换

因为目前 OBJ_LIST 类型的对象只会使用一种编码 OBJ_ENCODING_QUICKLIST ,所以不会发生编码的转换。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这