Redis设计与实现要点(5)--对象

Redis中有五种类型的对象,分别为字符串对象,列表对象,hash对象,集合对象和有序集合对象。这五种对象每种都用到了至少一种之前的数据结构(简单动态字符串,双端链表,字典,压缩列表,整数集合等)

Redis对象的结构:

Redis每一个对象均用一个redisObject结构表示,其结构如下:

  1. unsigned type:类型,是类型常量(REDIS_STRING, REDIS_LIST, REDIS_HASH,REDIS_SET, REDIS_ZSET)其中的一个。可以用TYPE命令查看
  2. unsigned encoding: 编码,是编码常量(REDIS_ENCODING_INT, REDIS_ENCODING_EMBSTR, REDIS_ENCODING_RAW,REDIS_ENCODING_ZIPLIST, REDIS_ENCODING_LINKEDLIST, REDIS_ENCODING_HT, REDIS_ENCODING_SKIPLIST, REDIS_ENCODING_INTSET)中的一个。可以用OBJECT ENCODING命令查看。
  3. void *ptr 指向底层数据结构的指针

下面介绍每一种类型的对象所对应的数据结构:

1.字符串类型

字符串类型的编码有三种:int,raw,或者embstr

  • 若一个字符串保存的是整数值,且这个整数值可以用Long来表示,则字符串对象会将整数值放在对象的ptr属性里面(将void转换为long)。并将编码设置为int
  • 若一个字符串的长度小于等于39字节,字符串对象会采取embstr编码存放
  • 若一个字符串的长度大于39字节,字符串对象会采用sds字符串存放数据,并将编码格式改为raw

raw和embstr均是采用sds(简单动态字符串)存储数据,不同的是raw编码格式存放时要采用两次申请空间(一次创建redisObject,一次sdshdr),而embstr会通过一次内存分配。(分配一块连续的空间,同样释放内存时也是一次释放)

在需要的时候字符串类型也会进行类型转换,比如对一个int类型append一段字符等。另外,embstr编码的字符串对象没有修改的方法,因此我们修改embstr字符串时他会首先把embstr转为raw编码类型。

2.列表类型

列表类型的编码有可能为ziplist(采用压缩列表作为底层实现,每个压缩列表节点保存一个链表元素)或者linkedlist(采用双端链表作为底层实现,每个双端链表节点都保存了一个字符串对象,而每个字符串对象都包含了一个列表元素)注意:字符串类型时Redis五种类型中唯一一种可以被气态类型嵌套的对象
但列表对象同时满足如下两个条件时(配置文件中的list-max-ziplist-value, list-max-ziplist-entries),列表对象采用ziplist编码:

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

其余不满足的列表均采用linkedlist编码,同样但一个ziplist编码列表对象发生改变以至于不满足这两个条件后也会发生类型转换。

3.哈希对象

哈希对象的编码可以是ziplist或者hashtable,当采用ziplist编码时,会先将保存键的压缩列表节点放到表尾,再将保存值的压缩列表节点放到表尾。(同一键值对的两个节点总是挨在一起,键在前值在后)
当hash对象同时满足以下两个条件(配置文件中的hash-max-ziplist-value, hash-max-ziplist-entries)时采用ziplist编码:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
  • 哈希对象包含的键值对数量小于512个
    否则采用hashtable编码,同样的,哈希表也存在类型转换。
4.集合对象

集合对象的编码可以是intset或者hashtable
当集合对象采用hashtable编码时,字典的每个键都是集合内的一个元素,字典的值全部为null
当集合对象同时满足如下两个条件(配置文件中set-max-intset-entries)时,采用intset编码

  • 集合对象保存的所有元素都是整数值
  • 集合对象保存的元素数量不超过512个
    否则采用hashtable编码,同样的,集合也存在类型转换。
5.有序集合

有序集合对象的编码可以是ziplist或者skiplist,若采用ziplist编码,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存成员,第二个节点保存元素分值;压缩列表内的元素按照分值排列,分值较小的元素靠近表头,较大靠近表尾。
而skiplist编码的有序集合对象采用zset结构作为底层实现,一个zset结构包含了一个字典和一个跳跃表。当我们采用skiplist编码时,首先他的跳跃表会按分值从小到大保存所有集合元素,每个跳跃表节点保存一个集合元素,Object属性保存成员,score属性包含分值;除此之外,字典也会保存元素的成员(字典的键保存成员,值保存分值),有序集合的元素是字符串类型,而分值为double类型,因此字典和跳跃表会引用同一个对象
当有序集合同时满足以下两个条件(配置文件中zset-max-ziplist-entries,zset-max-ziplist-value)时有序集合采用ziplist编码:

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

不满足条件的均采用skiplist编码

Redis多态,内存回收,对象共享和空转时间

Redis的多态主要指两种,一种是值命令可以操作任意一种类型的对象,例如DEL命令,EXPIRE命令,TYPR命令等。另一种是指一个命令再操作一个对象时,不用担心对象本身的数据结构。即一种是基于类型的,一种是基于编码的。
Redis的内存回收基于引用计数法。创建时对象引用计数初始化为1,此后被一个新程序使用时加一,反之减一。引用计数变为0时回收内存。
同时Redis的引用计数也带了另一个好处:对象共享,目前Redis再初始化时会创建0到9999(可更改redis.h/REDIS_SHARED_INTEGERS修改)字符串对象,当服务器用到时就会直接引用而不需要创建。
最后,redisObject除了之前介绍的type,encoding,ptr和refcount(引用计数)四个属性之外,redisObject结构包含的最后一个属性为lru属性,该属性记录了对象最后一次被命令访问的时间。可以通过OBJECT IDLETIME命令(OBJECT IDLETIME命令本身并不算被命令访问)来获得空转时间(当前时间减去lru);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值