第二章 对象

  1. 对象的类型与编码

  Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性,encoding属性和ptr属性:

   

  1.1 类型

  对于redis中保存的键值对来说,键总是一个字符串对象,而值则可以是字符串对象、列表对象、哈希对象、集合对象或者有序集合对象中的一种。

  

  字符串键指,键所对应的值为字符串对象;列表键指键所对应的值为列表对象

   

  1.2 编码与底层实现

  对象的ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。

  

  每种类型的对象使用了至少两种不同的编码。

   

  Redis可以根据不同的使用场景来为一个对象设置不同的编码,从而优化对象在某一场景下的效率。

  在列表对象包含元素较少时,Redis使用压缩列表作为列表对象的底层实现。

  • 压缩列表比双端列表更节约内存,并且在元素较少时,内存以连续会方式保存的压缩列表比起双端链表可以更快的被载入到内存中
  • 随着列表对象包含的元素越来越多,对象会将底层实现从压缩列表转向功能更强、也更适合保存大量元素的双端链表上面   

  2. 字符串对象

  字符串对象的编码可以是int、raw(字符串值得长度大于39字节)或者embstr(字符串长度小于39字节)。使用embstr编码的字符串对象来保存短字符串值有以下好处:

  • embstr编码将创建的字符串所需要的内存分配次数从raw编码的两次降为一次
  • 释放embstr编码的字符串对象只需要一次内存释放函数
  • embstr编码的字符串对象的所有数据都保存在一块连续的内存里,使用缓存读取更快

  

  2.1 编码的转换

  字符串对象的编码从int变为raw。

   

  embstr编码的字符串是只读的。embstr编码的字符串对象在执行修改命令后,总会变成一个raw编码的字符串对象。 

   

  2.2 字符串命令的实现

  

  3. 列表对象

  列表对象的编码是ziplist或者linkedlist。

  3.1 编码转换

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

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

  3.2 列表命令的实现

   

  4. 哈希对象

  哈希对象的编码可以是ziplist或者hashtable。

   

     

  4.1 编码转换

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

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

  4.2 哈希命令的实现

   

  5. 集合对象

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

   

   5.1 编码的转换

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

  • 集合对象保存的所有元素都是整数值
  • 集合对象保存的数量不超过512个

  5.2 集合命令的实现

   

        

  6. 有序集合对象

  有序集合对象的编码是ziplist后者skiplist。

  有序集合元素会同时被保存在字典和跳跃表中。

  如果只使用字典来实现有序集合,那么虽然能以O(1)复杂度查找成员分值,但是,因为字典以无序方式来保存集合元素,所以每次执行ZRANK、ZRANGE等命令时,程序都需要对字典保存的所有元素进行排序,需要至少O(NlogN)时间复杂度,以及额外O(N)的内存空间。使用跳跃表可以排位的时间复杂度为O(logN)。但如果没有字典,查找成员分值的复杂度会升高到O(logN)。为了让有序集合的查找和范围型操作尽可能快的执行,有序集合同时使用字典和跳跃表两种数据结构。

  

  6.1 编码的转化

  有序集合同时满足两个条件时,对象使用ziplist编码:

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

  6.2 有序集合命令的实现

   

   7. 类型检查与命令多态

  有些命令可以对任何类型的键执行,比如DEL命令,EXPIRE命令,RENAME命令,TYPE命令,OBJECT命令等。

  而另一些命令只能对特定类型的键执行:

  

  7.1 类型检查的实现

  类型特定命令所进行的类型检查是通过Redisobject结构的type属性来实现的:

  

  7.2 多态命令的实现

  

  8. 内存回收

  C语言不具备内存回收机制,Redis使用引用计数(reference counting)来实现内存回收机制。

  对象的引用计数信息会随着对象的使用状态而不断变化:

  • 在创建一个新对象时,引用计数的值会被初始化为1
  • 当对象被一个新程序使用时,引用计数值增1
  • 当对象不再被一个程序使用时,它的引用计数值会减一
  • 当对象的引用计数变为0时,对象所占用的内存会被释放

  9. 对象共享

  在Redis中,多个键共享同一个值对象需要以下两个步骤:

  1) 将数据库键的值指针指向一个现有的值对象

  2) 将被共享的值对象的引用计数增一

   

  目前,Redis会在初始化服务器时,创建0到9999的所有整数值对象,当服务器需要用到值0到9999的字符串对象时,服务器会使用这些共享对象。

  10. 对象的空转时长

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

  如果服务器启用了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,如果服务器占用的内存超过了maxmemory选项所设置的上限值,空转时长较高的键会优先被服务器释放,从而回收内存。

 

  

 

posted @ 2021-08-24 08:14  慕仙白  阅读(61)  评论(0)    收藏  举报