Redis 对象系统

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

redisObject

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

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

类型

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

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

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

1127.0.0.1:6379> SET msg "hello redis" 2OK 3127.0.0.1:6379> TYPE msg 4string

编码

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

1#define OBJ_ENCODING_RAW 0 /* 简单动态字符串 */ 2#define OBJ_ENCODING_INT 1 /* 整型 */ 3#define OBJ_ENCODING_HT 2 /* 字典 */ 4#define OBJ_ENCODING_ZIPMAP 3 /* 压缩字典(不再使用)*/ 5#define OBJ_ENCODING_LINKEDLIST 4 /* 双端链表(不再使用) */ 6#define OBJ_ENCODING_ZIPLIST 5 /* 压缩列表 */ 7#define OBJ_ENCODING_INTSET 6 /* 整数集合 */ 8#define OBJ_ENCODING_SKIPLIST 7 /* 跳表 */ 9#define OBJ_ENCODING_EMBSTR 8 /* Embedded 编码的简单动态字符串 */ 10#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 客户端中查看指定键的对象编码。

1127.0.0.1:6379> LPUSH DATABASE:LIST sqlserver mysql pgsql 2(integer) 3 3127.0.0.1:6379> OBJECT ENCODING DATABASE:LIST 4"quicklist"

空转时间

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

1127.0.0.1:6379> set msg "hello world" 2OK 3127.0.0.1:6379> OBJECT IDLETIME msg 4(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 类型的对象编码转换

相关配置项如下:

1hash-max-ziplist-entries 512 2hash-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 类型的对象编码转换

相关配置如下:

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

配置含义为:

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

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

OBJ_LIST 类型的对象编码转换

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

点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

一篇文章带你了解JavaScript日期

日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用