Redis内存淘汰指的是用户存储的一些键被可以被Redis主动地从实例中删除,从而产生读miss的情况,那么Redis为什么要有这种功能?这就是我们需要探究的设计初衷。Redis最常见的两种应用场景为缓存和持久存储,、、
作为Redis用户,我如何使用Redis提供的这个特性呢?看看下面配置
# maxmemory <bytes>
我们可以通过配置redis.conf中的maxmemory这个值来开启内存淘汰功能,至于这个值有什么意义,我们可以通过了解内存淘汰的过程来理解它的意义:
-
客户端发起了需要申请更多内存的命令(如set)。
-
Redis检查内存使用情况,如果已使用的内存大于maxmemory则开始根据用户配置的不同淘汰策略来淘汰内存(key),从而换取一定的内存。
-
如果上面都没问题,则这个命令执行成功。
maxmemory为0的时候表示我们对Redis的内存使用没有限制。
内存淘汰策略
内存淘汰只是Redis提供的一个功能,为了更好地实现这个功能,必须为不同的应用场景提供不同的策略,内存淘汰策略讲的是为实现内存淘汰我们具体怎么做,要解决的问题包括淘汰键空间如何选择?在键空间中淘汰键如何选择?
Redis提供了下面几种淘汰策略供用户选择,其中默认的策略为noeviction策略:
-
noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
-
allkeys-lru:在主键空间中,优先移除最近未使用的key。
-
volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。
-
allkeys-random:在主键空间中,随机移除某个key。
-
volatile-random:在设置了过期时间的键空间中,随机移除某个key。
-
volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。
这里补充一下主键空间和设置了过期时间的键空间,举个例子,假设我们有一批键存储在Redis中,则有那么一个哈希表用于存储这批键及其值,如果这批键中有一部分设置了过期时间,那么这批键还会被存储到另外一个哈希表中,这个哈希表中的值对应的是键被设置的过期时间。设置了过期时间的键空间为主键空间的子集。
如何选择淘汰策略
我们了解了Redis大概提供了这么几种淘汰策略,那么如何选择呢?淘汰策略的选择可以通过下面的配置指定:
# maxmemory-policy noeviction
但是这个值填什么呢?为解决这个问题,我们需要了解我们的应用请求对于Redis中存储的数据集的访问方式以及我们的诉求是什么。同时Redis也支持Runtime修改淘汰策略,这使得我们不需要重启Redis实例而实时的调整内存淘汰策略。
下面看看几种策略的适用场景:
-
allkeys-lru:如果我们的应用对缓存的访问符合幂律分布(也就是存在相对热点数据),或者我们不太清楚我们应用的缓存访问分布状况,我们可以选择allkeys-lru策略。
-
allkeys-random:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略。
-
volatile-ttl:这种策略使得我们可以向Redis提示哪些key更适合被eviction。
另外,volatile-lru策略和volatile-random策略适合我们将一个Redis实例既应用于缓存和又应用于持久化存储的时候,然而我们也可以通过使用两个Redis实例来达到相同的效果,值得一提的是将key设置过期时间实际上会消耗更多的内存,因此我们建议使用allkeys-lru策略从而更有效率的使用内存。
非精准的LRU
上面提到的LRU(Least Recently Used)策略,实际上Redis实现的LRU并不是可靠的LRU,也就是名义上我们使用LRU算法淘汰键,但是实际上被淘汰的键并不一定是真正的最久没用的,这里涉及到一个权衡的问题,如果需要在全部键空间内搜索最优解,则必然会增加系统的开销,Redis是单线程的,也就是同一个实例在每一个时刻只能服务于一个客户端,所以耗时的操作一定要谨慎。为了在一定成本内实现相对的LRU,早期的Redis版本是基于采样的LRU,也就是放弃全部键空间内搜索解改为采样空间搜索最优解。自从Redis3.0版本之后,Redis作者对于基于采样的LRU进行了一些优化,目的是在一定的成本内让结果更靠近真实的LRU。
总结
本文的内容主要还是问题驱动的,为介绍Redis内存淘汰机制,本文从问题出发,通过解决什么是Redis内存淘汰机制?Redis内存淘汰机制应用场景是什么?初衷是什么?我们怎么开启这个功能?我们可配哪些淘汰策略?淘汰策略如何选择?等问题来谈Redis内存淘汰机制,希望本文能给大家带来一定的收获和启发。