Redis key淘汰策略
当Redis作为缓存来用时,如果能让它在我们添加新数据时自动淘汰老数据,将会给我们带来很大的便利。这种行为在开发者社区里广为人知,毕竟memcached系统的默认行为。
本文涵盖了Redis maxmemory指令相关的主题内容,这条指令用于将内存使用限制到一个固定的值。除此之外,本文还着重介绍了Redis的LRU淘汰算法,与单纯的LRU算法基本一致。
maxmemory配置指令
maxmemory配置指令为Redis指定了固定的能用于存储数据的内存大小。你可以通过在redis.conf文件里设置这个值,也可以在运行时通过CONFIG SET指令来设置。
例如,如果要把内存使用限制在100mb,你可以在redis.conf文件里使用以下指令:
1 | maxmemory 100mb |
如果把maxmemory的值设置为0,则意味着对内存的使用是无限制的。这个是64位系统的默认行为,与此同时,32位系统隐含的内存使用上限不能超过3GB。
当内存使用量达到指定值时,淘汰策略的配置决定了默认如何淘汰旧数据。对于会导致超出内存限制的命令,Redis会返回错误,或者在新增数据时会淘汰旧数据来满足内存使用限制。
淘汰策略
当内存使用量达到maxmemory设置的值时,Redis将会执行指定的行为,而这个通过maxmemory-policy配置指令来指定。
以下这些是可以配置的策略值:
- noeviction:当内存使用量达到限定值时,新增的数据将不会被保存。如果一个数据库使用了主从复制,那么这将作用于主数据库。
- allkeys-lru:保留最近最常使用的key,移除最近最少使用(LRU)的key
- allkeys-lfu:保留频繁使用的key,移除最不频繁使用(LFU)的key
- volatile-lru:移除最近最少使用且expire值为true的key
- volatile-lfu:移除最近最不频繁使用且expire值为true的key
- allkeys-random:随机移除部分key来给新增的数据腾出空间
- volatile-random:随机移除部分expire值为true的key
- volatile-ttl:移除expire值为true而且存活时间(TTL)最短的key。
对于volatile-lru, volatile-lfu, volatile-random以及volatile-ttl这几个策略,当没有key满足先决条件时,这些策略将会和noeviction策略一样,不会进行key的移除。
选择正确的移除策略很重要,而这取决于应用访问Redis的模式,不过,在应用运行时,你也可以在Redis运行时去重新配置策略,并使用 Redis INFO输出监控缓存未命中和命中的数量来调整您的设置。
一般情况下,根据经验可以这么设置:
- 如果你希望你的请求受欢迎度呈幂律分布,也就是说,你希望某些数据会比其余的数据更频繁地访问,在这种情况下你可以使用allkeys-lru策略。在你不确定的时候,这是个不错的选择
- 如果你会循环访问,所有的key会被不断扫描,或者希望分布变得均匀,那么可以使用allkeys-random策略。
- 如果您希望能够在创建缓存对象时,通过使用不同的TTL值向Redis提示哪些可以优先移除,那么请使用volatile-ttl。
volatile-lru策略和volatile-random策略主要用于当你想使用单个实例进行缓存并拥有永远不会过期的key的场景。然而,运行两个Redis实例来解决这样的问题通常是一个更好的主意。
还值得注意的是,为某个key设置过期值会消耗内存,因此使用allkeys-lru之类的策略会更节省内存,因为在内存压力下无需为需要被移除的key设置expire值。
移除过程是怎样的
理解移除过程是很重要的:
- 客户端执行一个新命令,该命令导致数据新增。
- Redis检查内存的使用量,如果超过了设定的maxmemory限定值,则根据移除策略移除某些key。
- 新命令被执行,依此类推。
于是我们不断地越过内存限制的边界,然后通过移除key使得内存使用量保持在限制之下。
如果某个命令导致在一段时间内使用大量内存(例如将大集合交集保存到一个新key里),则内存限制可能会被明显超过。
近似的LRU算法
Redis的LRU算法并非是真正的实现。这意味着Redis无法挑选最适合的key来移除。Redis采用了近似的LRU算法,通过对少量的key进行采样,然后移除最适合的(很久前访问过)key。
然而,从Redis3.0开始对算法进行了改进,同时也采用了一个良好的候选池进行移除。这提高了算法的性能,使得它更接近真正的LRU算法。
Redis LRU算法的重要之处在于,可以通过更改样本数量来调整算法的精度,这个数量用于每次移除前的检查。这个参数通过以下配置命令进行控制:
1 | maxmemory-samples 5 |
Redis之所以没有用真正的LRU是因为它需要使用更多的内存。然而,对于使用Redis的应用程序来说,这种近似的实现是几乎等效的。下图对Redis所使用的LRU近似实现与实际的LRU进行了对比。