Redis对象类型

Redis基于基础的数据结构创建的对象:

  • 字符串对象、
  • 列表对象、
  • 哈希对象、
  • 集合对象
  • 有序集合对象。

对象回收:Redis对象系统实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候,这个对象所占用的内存就会被自动释放;Redis通过引用计数技术实现了对象共享机制,在适当的条件下通过让多个数据库键共享同一个内存对象来节约内存;

一、RedisObject

在server.h文件中,给出了RedisObject的结构体定义:

  1. typedef struct redisObject {
  2. unsigned type:;//类型
  3. unsigned encoding:;//编码
  4. unsigned lru:LRU_BITS; //访问时间lru
  5. int refcount;// 引用计数refcount
  6. void *ptr;//指向底层实现数据结构的指针
  7. } robj;
  • 类型type:

Redis的对象有五种类型,分别是string、hash、list、set和zset,type属性就是用来标识着五种数据类型。type占用4个bit位,其取值和类型对应如下:

  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
  • 编码类型encoding:

Redis对象的编码方式由encoding参数指定,也就是表示ptr指向的数据以何种数据结构作为底层实现。该字段也占用4个bit位。其取值和对应类型对应如下:

  1. #define OBJ_ENCODING_RAW 0 /* Raw representation */
  2. #define OBJ_ENCODING_INT 1 /* Encoded as integer */
  3. #define OBJ_ENCODING_HT 2 /* Encoded as hash table */
  4. #define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
  5. #define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
  6. #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
  7. #define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
  8. #define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
  9. #define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
  10. #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */

上述这几种编码类型与其底层实现如下表:

编码类型 底层实现
OBJ_ENCODING_RAW 简单动态字符串sds
OBJ_ENCODING_INT long类型的整数
OBJ_ENCODING_HT 字典dict
OBJ_ENCODING_LINKEDLIST 双端队列sdlist
OBJ_ENCODING_ZIPLIST 压缩列表ziplist
OBJ_ENCODING_INTSET 整数集合intset
OBJ_ENCODING_SKIPLIST 跳跃表skiplist和字典dict
OBJ_ENCODING_EMBSTR EMBSTR编码的简单动态字符串sds
OBJ_ENCODING_QUICKLIST 由双端链表和压缩列表构成的快速列表

Redis的每一种对象类型可以对应不同的编码方式,这就极大地提升了Redis的灵活性和效率。Redis可以根据不同的使用场景,来选择合适的编码方式,五种对象类型对应的底层编码方式如下表所示:

对象类型 编码方式

OBJ_STRING

字符串类型

OBJ_ENCODING_RAW ,OBJ_ENCODING_INT ,OBJ_ENCODING_EMBSTR

简单动态字符串sds;long类型的整数;EMBSTR编码的简单动态字符串sds

OBJ_LIST

列表类型

OBJ_ENCODING_LINKEDLIST ,OBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_QUICKLIST

双端队列sdlist;压缩列表ziplist;由双端链表和压缩列表构成的快速列表

OBJ_SET

集合类型

OBJ_ENCODING_INTSET ,OBJ_ENCODING_HT

整数集合intset、字典dict

OBJ_ZSET

有序集合类型

OBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_SKIPLIST

压缩列表ziplist、跳跃表skiplist和字典dict

OBJ_HASH

哈希类型

OBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_HT

压缩列表ziplist、字典dict

  • 访问时间lru

lru表示该对象最后一次被访问的时间,其占用24个bit位。保存该值的目的是为了计算该对象的空转时长,便于后续根据空转时长来决定是否释放该键,回收内存。键的空转时长还有另外一项作用:如果服务器打开了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过了maxmemory选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存。

  • 引用计数refcount

C语言不具备自动内存回收机制,所以Redis对每一个对象设定了引用计数refcount字段,程序通过该字段的信息,在适当的时候自动释放内存进行内存回收。此功能与C++的智能指针相似。

  1. 当创建一个对象时,其引用计数初始化为1;
  2. 当这个对象被一个新程序使用时,其引用计数加1;
  3. 当这个对象不再被一个程序使用时,其引用计数减1;
  4. 当引用计数为0时,释放该对象,回收内存。

二、t_String 字符串类型

字符串是Redis中最为常见的数据存储类型,其底层实现是简单动态字符串sds,因此,该字符串类型是二进制安全的,这就意味着它可以接受任何格式的数据。另外,Redis规定,字符串类型最多可以容纳的数据长度为512M。Redis提供了下列函数,来检测字符串键的大小。

  1. static int checkStringLength(client *c, long long size) {
  2. // 超出了512M,就直接报错
  3. if (size > **) {
  4. addReplyError(c,"string exceeds maximum allowed size (512MB)");
  5. return C_ERR;
  6. }
  7. return C_OK;
  8. }

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

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

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

  • 如果字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于32字节,那么字符串对象将使用embstr编码的方式来保存这个字符串值。embstr编码是专门用于保存短字符串的一种优化编码方式,embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构。

三、t_list 列表对象类型

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

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

不能满足这两个条件的列表对象需要使用linkedlist编码。

以上两个条件的上限值是可以修改的,配置文件中关于list-max-ziplist-value选项和list-max-ziplist-entries

  • ziplist编码的列表对象使用压缩列表作为底层实现,每个压缩列表节点(entry)保存了一个列表元素。

  • 另一方面,linkedlist编码的列表对象使用双端链表作为底层实现,每个双端链表节点(node)都保存了一个字符串对象,而每个字符串对象都保存了一个列表元素。

四、t_hash 哈希对象类型

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

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

不能满足这两个条件的哈希对象需要使用hashtable编码。

这两个条件的上限值是可以修改的,配置文件中关于hash-max-ziplist-value选项和hash-max-ziplist-entrie

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

每当有新的键值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾,因此:

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

  • hashtable编码的哈希对象使用字典作为底层实现
  1. 字典的每个键都是一个字符串对象,对象中保存了键值对的键;
  2. 字典的每个值都是一个字符串对象,对象中保存了键值对的值。

五、t_set 集合对象类型

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

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

  1. 集合对象保存的所有元素都是整数值;
  2. 集合对象保存的元素数量不超过512个。

不能满足这两个条件的集合对象需要使用hashtable编码。第二个条件的上限值是可以修改的,配置文件中关于set-max-intset-entries选项的说明

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

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

六、t_zset 集合对象类型

  • ziplist编码的压缩列表对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)。

  压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,而分值较大的元素则被放置在靠近表尾的方向。

  • zset结构中的zsl跳跃表

zsl跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素。每个跳跃表节点都保存了一个集合元素:跳跃表节点的object属性保存了元素的成员,而跳跃表节点的score属性则保存了元素的分值。通过这个跳跃表,程序可以对有序集合进行范围型操作。

  除此之外,zset结构中的dict字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素:字典的键保存了元素的成员,而字典的值则保存了元素的分值。通过这个字典,程序可以用O(1)复杂度查找给定成员的分值,ZSCORE命令就是根据这一特性实现的,而很多其他有序集合命令都在实现的内部用到了这一特性。

  在理论上,有序集合可以单独使用字典或者跳跃表的其中一种数据结构来实现,但无论单独使用字典还是跳跃表,在性能上对比起同时使用字典和跳跃表都会有所降低。举个例子,如果我们只使用字典来实现有序集合,那么虽然以O(1)复杂度查找成员的分值这一特性会被保留,但是,因为字典以无序的方式来保存集合元素,所以每次在执行范围型操作——比如ZRANK、ZRANGE等命令时,程序都需要对字典保存的所有元素进行排序,完成这种排序需要至少O(NlogN)时间复杂度,以及额外的O(N)内存空间(因为要创建一个数组来保存排序后的元素)。

另一方面,如果我们只使用跳跃表来实现有序集合,那么跳跃表执行范围型操作的所有优点都会被保留,但因为没有了字典,所以根据成员查找分值这一操作的复杂度将从O(1)上升为O(logN)。因为以上原因,为了让有序集合的查找和范围型操作都尽可能快地执行,Redis选择了同时使用字典和跳跃表两种数据结构来实现有序集合。

七、类型检查与命令多态

Redis中用于操作键的命令基本上可以分为两种类型。

  • 其中一种命令可以对任何类型的键执行,比如说DEL命令、EXPIRE命令、RENAME命令、TYPE命令、OBJECT命令等;
  • 而另一种命令只能对特定类型的键执行,比如说:

  ❑SET、GET、APPEND、STRLEN等命令只能对字符串键执行;

  ❑HDEL、HSET、HGET、HLEN等命令只能对哈希键执行;

  ❑RPUSH、LPOP、LINSERT、LLEN等命令只能对列表键执行;

  ❑SADD、SPOP、SINTER、SCARD等命令只能对集合键执行;

  ❑ZADD、ZCARD、ZRANK、ZSCORE等命令只能对有序集合键执行;

Redis对象类型的更多相关文章

  1. Redis对象的设计与实现

    一.Redis对象结构Redis中的每个对象都由一个redisObject结构表示: typedef struct redisObject { unsigned type;//类型 unsigned ...

  2. Redis之对象篇——Redis对象系统简介

    Redis之对象篇--Redis对象系统简介 前言     之前几篇文章,简单介绍 Redis用到的所有主要数据结构,简单动态字符串(SDS).双端链表.字典.压缩列表.整数集合.跳跃表. 图解Red ...

  3. redis 系列9 对象类型(字符串,哈希,列表,集合,有序集合)与数据结构关系

    一.概述 在前面章节中,主要了解了 Redis用到的主要数据结构,包括:简单动态字符串.链表(双端链表).字典.跳跃表. 整数集合.压缩列表(后面再了解).Redis没有直接使用这些数据结构来实现键值 ...

  4. [redis读书笔记] 第一部分 数据结构与对象 对象类型

    - 从前面redis的基本数据结构来看,可以看出,redis都是在基本结构(string)的基础上,封装了一层统计的结构(SDS),这样让对基本结构的访问能够更快更准确,提高可控制度. - redis ...

  5. 浅谈Redis五个对象类型的底层原理

    本博客强烈推荐: Java电子书高清PDF集合免费下载 https://www.cnblogs.com/yuxiang1/p/12099324.html Redis是一种key/value型数据库,其 ...

  6. 一文读懂Redis常见对象类型的底层数据结构

    Redis是一个基于内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.Redis支持五种常见对象类型:字符串(String).哈希(Hash).列表(List).集合(Set)以及有序集合( ...

  7. 15天玩转redis —— 第五篇 集合对象类型

    这篇我们来看看Redis五大类型中的第四大类型:“集合类型”,集合类型还是蛮有意思的,第一个是因为它算是只使用key的Dictionary简易版, 这样说来的话,它就比Dictionary节省很多内存 ...

  8. Redis学习笔记(2) Redis基础类型及命令之一

    1. 基础命令 (1) 获取符合规则的键名列表 格式为:KEYS pattern 其中pattern表示支持通配符 # 建立一个名为bar的键 > SET bar OK # 获取Redis所有键 ...

  9. redis五大类型用法

    Redis五大类型:字符串(String).哈希/散列/字典(Hash).列表(List).集合(Set).有序集合(sorted set)五种Controller:@Resource RedisTe ...

随机推荐

  1. N点虚拟主机管理系统如何使用?

    有朋友问起N点虚拟主机管理系统怎么用呢?下面大概整理下他的使用方法,咱们来看看吧.          在讲如何使用N点虚拟主机管理系统之前,我们先来了解一下N点虚拟主机管理系统的介绍. ​     N ...

  2. Linux Kernel 4.11首个候选版本开放下载

    Linus Torvalds宣布了即将到来的Linux Kernel 4.11内核分支的首个候选(RC)版本,用户可下载.编译并在自己的GNU/Linux发行版本中进行测试.Linus Torvald ...

  3. Zip压缩/解压缩(文件夹)

    #PS2.0压缩为.zip文件: $zip = "D:\audit_log\test.zip"New-Item $zip -ItemType file$shellApplicati ...

  4. cygwin64-安装包管理工具

    1.dos command, install pkg $ setup-x86_64.exe -q -P curl $ setup-x86_64.exe -q -P lynx 2. cygwin64 c ...

  5. Exchange2016 & Skype for business集成之二 OWA集成IM

    Microsoft Outlook Web App 和IM集成部署或升级Exchange server 2016与Skype for business 2015后使用原来2013版本方法集成OWA网页 ...

  6. windows下sqli-labs的搭建及学习(POST篇)

    windows下sqli-labs的搭建及学习(GET篇): http://blog.csdn.net/sherlock17/article/details/64454449 Less-11:基于错误 ...

  7. 如何去掉android的标题栏

    利用eclipse创建一个android工程向导后默认的会给app加上一个标题栏,如下图如何去掉红色的标题栏呢?从网上找的有下面的几种方法.1.通过代码去掉在Activity的onCreate中加入如 ...

  8. 外网访问用azure虚拟机搭建的网站

    1.Ubuntu+Apache+PHP的环境搭建(此处省去了mysql的步骤) 在azure上搭建上述的开发环境和在本地PC搭建是一样的步骤,具体介绍请参看这里. 2.从外网访问 注意,这一步的前提是 ...

  9. Shell的egrep、grep、fgrep命令的使用

    Linux egrep 命令用于在文件内查找指定的字符串.与grep-E使用语法及参数相似,不同点在于解读字符串的方法. 语法: egrep [范本模式] [文件或目录] 参数说明: [范本模式]:查 ...

  10. JS实现键盘监听(包括组合键)

    依然使用案例驱动~案例是学习的最好实践!   <html>   <head>   <meta http-equiv="Content-Type" co ...