Redis 对象共享、内存回收、空转时长

内存回收

C 语言不具备内存回收功能,所以 Redis 构建了一个由引用计数实现的内存回收机制,通过这一机制,程序可以通过跟踪对象的引用技术信息,在适当的时候自动释放对象并进行内存回收

每个对象的引用计数信息由 redisObject 结构的 refcount 属性记录:

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

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

对象共享

除了用于实现引用计数内存回收机制之外,对象的引用计数属性还带有对象共享的作用

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

  1. 将数据库键的值指针指向一个现有的值对象
  2. 将被共享的值对象的引用计数加一

共享对象机制对于节约内存很有帮助,数据库中保存的相同值对象越多,对象共享机制就能节约越多内存

Redis 在初始化服务器时,会创建一万个字符串对象,这些对象包含了 0 到 9999 的所有整数值,当服务器需要用到值为 0 到 9999 的字符串对象时,服务器就会使用这些共享对象

共享对象不仅只有字符串键可以使用,那些在数据结构中嵌套类字符串对象的对象(linkedlist 编码的列表对象、hashtable 编码的哈希对象、hashtable 编码的集合对象以及 zset 编码的有序集合对象)都可以使用这些共享对象

为什么 redis 不共享包含字符串的对象

当服务器考虑将一个共享对象设置为键的值对象时,需要先检查给定的感谢对象和键想创建的目标对象是否完全相同,只有完全相同才能共享。而一个共享对象保存的值越复杂,验证共享对象和目标对象是否相同所需的复杂度就会越高,消耗的 CPU 时间也越多

  • 如果共享对象是保存整数值的字符串对象,那么验证操作的复杂度为 O(1)
  • 如果共享对象是保存字符串值的字符串对象,那么验证操作的复杂度为 O(N)
  • 如果共享对象是包含了多个值(或者对象)的对象,比如列表对象或者哈希对象,那么验证操作的复杂度为 O(N^2)

因此,尽管共享更复杂的对象可以节约更多内存,但受到 CPU 时间的限制,Redis 只对包含整数值的字符串对象进行共享

对象的空转时长

除了 type、encoding、ptr 和 refcount 属性外,redisObject 结构包含的最后一个属性为 lru 属性,该属性记录了对象最后一次被命令程序访问的时间

OBJECT IDLETIME 命令可以打印出给定键的空转时长,这一空转时长就是通过将当前时间减去键的值对象的 lru 时间计算得出的

键的空转时长还有另外一项作用:如果服务器打开了 maxmemory 选项,并且服务器用于回收内存的算法为 volatile-lru 或者 allkeys-lru,那么当服务器占用的内存数超过了 maxmemory 选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而内存回收

你可能感兴趣的:(Redis 对象共享、内存回收、空转时长)