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



设置内存上限


config set maxmemory xx,maxmemory限制的是Redis真正使用的内存,也就是used_memory对应的内存,由于内存碎片的存在,实际消耗的内存可能会比maxmemory大,实际使用时要小心这部分内存溢出。



内存回收策略


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

 - 删除到达过期时间的键对象。

 - 内存使用达到maxmemory上限时触发内存溢出控制策略。



删除过期键对象


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


惰性删除:当客户端读取带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空。但当过期键一直没有访问无法得到及时删除,从而导致内存不能及时释放。Redis还提供另一种定时任务删除机制作为补充。


定时任务删除:Redis内存维护一个定时任务,默认每秒运行10次(通过hz控制)。定时任务中删除过期键逻辑采用了自适应算法,根据键的过期比例,使用快慢速率模式回收键。

# Redis calls an internal function to perform many background tasks, like
# closing connections of clients in timeout, purging expired keys that are
# never requested, and so forth.
#
# Not all tasks are performed with the same frequency, but Redis checks for
# tasks to perform according to the specified "hz" value.
#
# By default "hz" is set to 10. Raising the value will use more CPU when
# Redis is idle, but at the same time will make Redis more responsive when
# there are many keys expiring at the same time, and timeouts may be
# handled with more precision.
#
# The range is between 1 and 500, however a value over 100 is usually not
# a good idea. Most users should use the default of 10 and raise this up to
# 100 only in environments where very low latency is required.
hz 10


内存溢出控制策略


当Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略,具体控制策略由maxmemory-policy参数控制。


# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key according to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations

# Note: with any of the above policies, Redis will return an error on write
#  operations, when there are no suitable keys for eviction.

#  At the date of writing these commands are: set setnx setex append      
#  incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd     
#  sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby     
#  zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby     
#  getset mset msetnx exec sort      

# The default is:
# maxmemory-policy noeviction


Redis在使用LRU算法回收过期键时,并不是采用维护TTL链表,而是使用采样的方式。采样的多少由maxmemory-samples控制。


# LRU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can tune it for speed or
# accuracy. For default Redis will check five keys and pick the one that was
# used less recently, you can change the sample size using the following
# configuration directive.

# The default of 5 produces good enough results. 10 Approximates very closely
# true LRU but costs a bit more CPU. 3 is very fast but not very accurate.

# maxmemory-samples 5

每次Redis执行命令时若设置了maxmemory参数,都会尝试执行内存回收操作。当Redis一直工作在内存溢出(used_memory>maxmemory)状态下,且设置非noeviction策略时,会频繁触发内存回收操作,主要包括查找可回收的键和删除键的开销,影响Redis的性能。如果当前Redis有从节点时,回收内存操作对应的删除命令会同步到从节点,导致写放大的问题。