Cais~ 阅读(23) 评论(0)

Redis主要通过控制内存上线和回收策略来实现内存管理。

1. 设置内存上限

    redis使用maxmemory参数限制最大可用内存。限制的目的主要有:

  • 用户缓存场景,当超出内存上限maxmemory时使用LRU等删除策略释放空间。
  • 防止所用内存超过服务器物理内存。

需要注意,maxmemory限制的是redis实际使用的内存量,也就是used_memory统计项对应的内存。由于内存碎片率的存在,实际消耗的内存可能会比maxmemory设置的更大,实际使用时要小心这部分内存溢出。得益于redis单线程架构和内存限制机制,即使没有采用虚拟化,不同的redis进程之间也可以很好的实现cpu和内存的隔离性。

 

2. 动态调整内存上限

    redis的内存上限可以通过config set maxmemory进行动态修改,实现在当前服务器下动态伸缩redis内存的目的。单如果超过系统物理内存限制就需要采用在线迁移数据或者复制切换服务器来达到扩容的目的。

 

3. 内存回收策略

    redis的内存回收机制主要体现在以下两个方面:

  • 删除到达过期时间的键对象
  • 内存使用达到maxmemory上限时触发内存溢出控制策略

3.1 删除过期键对象

    redis所有键都可以设置过期属性,内部保存在过期字典中。由于进程内保存大量的键,维护每个键精准的过期机制会导致消耗大量的CPU,对于单线程的redis来说成本过高,因此redis采用惰性删除和定时任务删除机制实现过期键的内存回收。

  • 惰性删除:当你去操作一个键(譬如 get name),redis首先会检查这个键是否关联了一个超时时间,如果有,则检查是否超时,若超时则返回空,否则返回相应的值;这种策略是出于节省CPU成本考虑,不需要单独维护TTL链表来处理过期键的删除。但单独用这种方式存在内存泄漏的风险,当过期键一直没有被访问将无法得到及时删除,从而导致内存不能及时释放。
  • 定时删除:redis中有个时间事件,默认每秒运行10次(通过配置hz控制),它会清理数据库中已经过期的键(redis会限定该操作占用的时间,避免阻塞客户端的请求)。

 

3.2 内存溢出控制策略

    当redis所用内达到maxmemory上限时会触发相应的溢出控制策略。具体策略受maxmemory-policy参数控制,redis支持6种策略,如下所示:

  • noeviction:默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息(error)OOM command not allowed when used memory,此时redis只响应读操作。
  • volatile-lru:根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。
  • allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出空间为止。
  • allkeys-random:随机删除所有键,直到腾出足够空间为止。
  • volatile-random:随机删除过期键,直到腾出足够空间为止。
  • volatile-ttl:根据键值对象的ttl属性,删除最近将要过期的数据,如果没有,回退到noeviction策略。

    内存溢出控制策略可以采用config set maxmemory-policy {policy}动态配置。redis支持丰富的内存溢出应对策略,可以根据实际需求灵活定制,比如当设置valatile-lru策略时,保证具有过期属性的键可以根据LRU(Least Recently Used)剔除,而未设置超时的键可以永久保留。还可以采用allkeys-lru策略把redis变为纯缓存服务器使用。当redis因为内存溢出删除键时,可以通过执行info stats命令查看evicted_keys指标找出当前redis服务器已剔除的键数量。如果redis一直工作在内存溢出(used_memory>maxmemory)的状态下且设置非noeviction策略时,会频繁的触发内存回收操作,会影响redis服务器性能,主要包括查找可回收键和删除键的开销,如果当前redis有从节点,回收内存操作对应的删除命令会同步到从节点,导致写放大的问题。

    对于需要收缩redis内存的场景,可以通过调小maxmemory来实现快速回收。此操作会导致数据丢失和短暂的阻塞问题,一般在缓存场景下使用。