把 Redis 当作缓存使用时,有时你能够方便的让它在新数据时自动逐出旧数据。这一点你们都比较清楚,由于 memcached 默认也会这么干redis
Redis 仅支持 LRU 逐出策略。下文主要讲述 Redis maxmemory 指令,这个指令用于限定内存使用量,以及讲述了 Redis 使用到的 LRU 算法,这是一种近似LRU算法。算法
maxmemory 配置指令缓存
maxmemory 指令用于限定内存使用量。能够在 redis.conf 文件中设置,也能够经过 CONFIG SET 命令在运行时设置。dom
例如在 redis.conf 文件中添加以下指令将内存限定在 100M 之内memcached
maxmemory 100mb性能
设置成 0 时表示无限制。64位系统下默认无限制,32位系统则强制指定为 3GBspa
当内存使用达到限定值时,能够选择几种不一样的策略。例如 Redis 能够在调用指令时直接返回错误(这些指令会致使更多内存使用),或者是逐出老数据,给新数据纳出空间,让内存占用保持在限定一下。对象
逐出策略ip
具体的逐出策略经过 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> 指令能够方便的设置取样数。