关于:Redis 的内存回收策略、Key 失效机制
关于
在生产环境我们偶尔会遇到 Redis 服务器内存不够的情况,那对于这种情况 Redis 的内存是如何回收处理的呢?另外对于带有过期时间的 Key Redis 又是如何处理的呢?
Redis 内存设置
如果要设置 Redis 的最大内存大小,依旧可以通过两个方式:
- 只需要在配置文件“redis.conf”中配置一行“maxmemory xxx”即可,
- 如果设置为 0 :64位的操作系统默认是没有内存限制,而32位的操作系统隐式限制为 3 GB。
- 或者我们通过“config set”命令在运行时动态配置 Redis 的内存大小。
// 获取最大内存限制 config get maxmemory 1) "maxmemory" 2) "0" // 设置最大的内存限制 config set maxmemory 100mb
内存回收策略
Redis 提供了多种内存回收策略:
- “volatile-lru”:在“所有带有过期时间的 key”中使用 LRU 算法淘汰数据;【带有过期时间的 key】
- “volatile-random”:在“所有带有过期时间的 key”中随机淘汰数据;【带有过期时间的 key】
- “volatile-ttl”:在“所有带有过期时间的 key”中,淘汰会最早过期的数据;【带有过期时间的 key】
- “alkeys-lru”:在“所有的 key”中使用 LRU 算法淘汰数据;【所有key】
- “allkeys-random”:在“所有的 key”中随机淘汰数据;【所有key】
- “noeviction”:不回收,不会清除旧数据;
- 达到最大内存时,对于写请求不再提供服务,直接返回错误。(DEL请求和部分特殊请求除外)
- LRU:“Least Recently Used”,即“最近最少使用”。
- “volatile-lru”,“volatile-random”,“volatile-ttl”这几种情况(根据过期时间淘汰),在 Redis 中没有带有过期 Key 的时候跟“noeviction”策略是一样的。
配置
同样可以通过两种方式修改内存回收策略:
- 配置文件:
- 命令:
// 获取内存淘汰策略 可以看到默认是noeviction config get maxmemory-policy 1) "maxmemory-policy" 2) "noeviction" // 修改内存驱逐策略 config set maxmemory-policy allkeys-lru
淘汰策略是可以动态调整的,调整的时候是不需要重启的:
“To pick the right eviction policy is important depending on the access pattern of your application, however you can reconfigure the policy at runtime while the application is running, and monitor the number of cache misses and hits using the Redis INFO output in order to tune your setup.”
策略的执行过程
过期策略按照以下步骤执行:
- 客户端运行命令,添加数据申请内存;
- Redis 检查内存的使用情况:如果超过最大限制,则执行内存淘汰策略;
- 继续执行命令。
“近似 LRU”(随机采样 LRU)
Redis 中的 LRU 算法不是精确的 LRU 算法,而是一种近似的 LRU 算法(经过采样的 LRU)。
近似 LRU 算法通过随机采样法淘汰数据:每次随机出5(默认)个 key,从里面淘汰掉最近最少使用的 key。
- 可以通过在配置文件中设置“maxmemory-samples 5”来设置采样的大小(默认值为 5,可以自行调整)。
- Redis 为了实现近似 LRU 算法,给每个 key 增加了一个额外增加了一个 24 bit 的字段,用来存储该 key 最后一次被访问的时间。【???】
官方提供的采用对比如下,我们可以看到
Redis 3.0 对近似 LRU 算法的优化
在 Redis 3.x 以上的版本的中做过优化,目前的近似 LRU 算法以及提升了很大的效率。
新算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序:
- 第一次随机选取的 key 都会放入池中,随后每次随机选取的 key 只有在访问时间小于池中最小的时间才会放入池中,直到候选池被放满;
- 当放满后,如果有新的 key 需要放入,则将池中最后访问时间最大(最近被访问)的移除。
新旧算法的对比
Redis官方文档给出的新旧算法对比结果:
其中:
- 浅灰色是被淘汰的数据
- 灰色是没有被淘汰掉的老数据
- 绿色是新加入的数据
从上图,可以看出:3.0 的效果明显比 2.8 的要得多,并且,取样数越大,越接近标准的 LRU 算法。
Redis 为什么不使用真正的 LRU
Redis 之所以不采样实际的 LRU 算法,是因为会耗费很多的内存(每个 key 还需要保存前后 key 的地址)。
LFU:新的回收策略
LFU(“Least Frequently Used”)算法,核心思想是根据 key 的最近被访问的频率进行淘汰:很少被访问的优先被淘汰,被访问的多的则被留下来。
- Redis4.0 里面新加的一种淘汰策略。
LFU 实现主要依靠于两个参数:
- 计算器:计算器取值范围为 0-255,访问次数越高对应的取值越高;
- 衰减周期:表示每过一定的时间,就对计算器进行减值。
有如下两个参数:
// 因子越大,改要使频率计数器所需的次数越多???
lfu-log-factor 10
// 每隔1分钟计算器减 1;如果是 0 的话表示每次扫描时总是使计数器衰减
lfu-decay-time 1
LFU 提供的两种回收策略
- volatile-lfu:在“设置了过期时间的 key”中使用 LFU 算法淘汰key;
- allkeys-lfu:在“所有的 key”中使用 LFU 算法淘汰数据;
LFU 与 LRU
LFU 算法能更好的表示一个 key 被访问的热度。
LRU 可能出现的问题:一个 key 很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些 key 将来是很有可能被访问到的则被淘汰了。
- 如果使用 LFU 算法则不会出现这种情况,因为:使用一次并不会使一个 key 成为热点数据。
Key 失效机制
Key 的失效机制,就是如何清除带有过期时间的 key。【缓存更新】
Redis 有两种“Key 失效机制”:
- 惰性策略(被动方式):在每次访问的时候判断该 key 是否到达过期时间了,过期了就删除掉。
- 对内存不太友好;对 CPU 友好;
- 但是存在一个问题:如果这些过期的 key 我们再也不会访问,那么永远就不会删除了。
- 定时策略(主动方式):随机抽取一部分的 key 进行校验,如果已经失效,就删除淘汰。
- 对内存友好(因为可以及时清理过期的 key);但对 CPU 不友好(每个带有过期时间的 key 都需要一个定时器,会占用很多的 CPU);
Redis 在实现的时候,会将以上两种方式结合使用:
Specifically this is what Redis does 10 times per second: 1、Test 20 random keys from the set of keys with an associated expire. 2、Delete all the keys found expired. 3、If more than 25% of keys were expired, start again from step 1.
即:Redis 会在有过期时间的 Key 集合中随机 20 个出来,删掉已经过期的 Key;如果过期比例仍超过 25%,重新执行此操作。
- 每秒中会执行 10 个这样的操作。
设置带有过期时间的 key
示例:
从上面的截图中我们可以看到右下角有个 TTL,并且每次刷新都是在减少的,说明我们设置带有过期时间的 key 成功了。