把 Redis 当做缓存使用时,有时你可以方便的让它在新数据时自动逐出旧数据。这一点大家都比较清楚,因为 memcached 默认也会这么干
Redis 仅支持 LRU 逐出策略。下文主要讲述 Redis maxmemory 指令,这个指令用于限定内存使用量,以及讲述了 Redis 使用到的 LRU 算法,这是一种近似LRU算法。
-
maxmemory 配置指令
maxmemory 指令用于限定内存使用量。可以在 redis.conf 文件中设置,也可以通过 CONFIG SET 命令在运行时设置。
-
例如在 redis.conf 文件中添加如下指令将内存限定在 100M 以内
maxmemory 100mb
设置成 0 时表示无限制。64位系统下默认无限制,32位系统则强制指定为 3GB
当内存使用达到限定值时,可以选择几种不同的策略。例如 Redis 可以在调用指令时直接返回错误(这些指令会导致更多内存使用),或者是逐出老数据,给新数据纳出空间,让内存占用保持在限定一下。
-
逐出策略
-
具体的逐出策略通过 maxmemory-policy 指令进行配置,主要有如下策略:
noeviction:调用某些指令时返回错误(主要是绝大多数的写指令,DEL 和 部分其他指令不包括)
allkeys-lru:对全键进行LRU
volatile-lru:对指定了过期时间(expire set)的键进行LRU
allkeys-random:对全键进行随机逐出
volatile-random:对指定了过期时间的键随机逐出
volatile-ttl:对指定了过期时间,并且 TTL 较短的键进行逐出
volatile-* 系列指令在无键值满足条件时(例如未设置过期时间),表现为 noeviction
不同的应用选择不同的逐出策略,当然你可以根据命中率(INFO指令)在运行时动态调整策略。
-
可以参考以下的经验法则:
allkeys-lru,预期的请求符合长尾理论。或是啥都不懂时配成这个不会太差
allkeys-random,会持续轮询所有的键。或者预期的请求符合均匀分布
volatile-ttl,在生成缓存对象时指定不同的 ttl 值,所以你得控制好
你在单实例上同时存储缓存数据,以及一些持久化数据时,volatile-lru 和 volatile-random 会比较适合。但是通常建议缓存数据和持久化数据用不同的实例存储。
另外,对一个键设置过期时间会占用额外的内存,所以在内存压力较大时 allkeys-lru 的内存使用率会较好。
-
-
逐出过程是如何实现的
-
最好从以下几个方面来了解逐出过程
客户端运行了一个消耗内存的指令
Redis 检查内存占用后发现超限,执行逐出策略
执行一个新的指令,如此循环
即反复的让 Redis 的内存占用在限定值上下波动,来观察和验证逐出策略
当一个指令消耗较多内存时,一定时间范围内可以观察到明显的内存超限
-
-
近似LRU算法
Redis 使用的 LRU 算法是一个近似实现,即逐出Key并不一定真正访问最少的键。它采用的方式是,对逐出范围内的键进行采样,然后对样本进行逐出。什么鬼。
在 Redis 3.0中有了一些改进,在提升性能的同时,让近似LRU的结果更加接近真实LRU。
-
Redis LRU挺重要的一点是,你可以调整算法精度,即调整每次逐出时的取样数。可以通过这个指令进行调整:
maxmemory-samples 5
Redis 使用 近似LRU 的目的主要还是为了节省内存,对于应用来说,近似与真实,实际上是等效的。
在 Redis 中填充满指定数目的数据,顺序访问所有的键,在LRU下,第一个键是最佳逐出对象。然后增加50%的键值,逐出一半的老数据。
-
图中的三种点形成了三条不同的条纹
浅灰色表示已经逐出的
灰色表示未被逐出的
绿色表示新增的
理论LRU的结果完全符合预期,前一半的老数据逐出。Redis LRU 则是概率上的逐出老数据。
可以看到,取样数为5时,Redis 3.0 比 Redis 2.8 效果要好很多,2.8逐出了不少刚刚被访问过的数据。取样数为10时,Redis 3.0 的表现跟理论LRU就非常接近了。
如果请求符合长尾法则,那么真实LRU与Redis LRU之间表现基本无差异。
你可以在增加一定CPU消耗的情况下,提高取样数,然后检查命中率是否有变化
在生产环境,通过 CONFIG SET maxmemory-samples <count> 指令可以方便的设置取样数。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。