始因
有时候线上可能会遇到这样的问题:
明明我设置了对应的 key 以及超时时间,但是在使用的过程当中发现对应的 key 丢失了,尤其是在用户账号登录状态保持有效期的场景下,会越发的明显。即:一个用户正常登录会产生一个有效期为一天的 token,这样用户再次进入网站是不需要登录的。但是发生 key 丢失问题就会导致用户需要频繁的重新登录,用户体验相当不好。导致这种问题的原因一般有以下两种情况:
1. token 生成时出现逻辑问题
2. 验证 token 时出问题了
对于上线稳定的项目来说,发生 1 的概率基本为 0。那么会立马定位到 2 的情况。这种情况就会引发我们今天讨论的问题:
redis 如何自动清理过期 key,以及对应 key 没有过期但是也会被清理掉呢?
近似 LRU
在解释近似LRU之前,先来简单了解一下LRU。
当Redis的内存占用超过我们设置的 maxmemory 时,会把长时间没有使用的key清理掉。按照 LRU算法,我们需要对所有key(也可以设置成只淘汰有过期时间的key)按照空闲时间进行排序,然后淘汰掉空闲时间最大的那部分数据,使得Redis的内存占用降到一个合理的值。
LRU算法的缺点:
1. 我们需要维护一个全部(或只有过期时间)key的列表,还要按照最近使用时间排序。这会消耗大量内存
2. 每次操作 key 时更新对应维护列表的排序也会占用额外的CPU资源。
对于Redis这样对性能要求很高的系统来说是不被允许的。
因此,Redis采用了一种 近似LRU 的算法。当Redis接收到新的写入命令,而内存又不够时,就会触发 近似LRU 算法来强制清理一些key。
具体清理的步骤是:
1. Redis会对 key 进行采样,通常是取5个,然后会把过期的key放到我们上面说的“过期池”中
2. 过期池中的 key 是按照空闲时间来排序的,Redis 会优先清理掉空闲时间最长的 key,直到内存小于 maxmemory。
清理策略
最后我们来看一下Redis支持的几种清理策略
1. noeviction:不会继续处理写请求(DEL可以继续处理)。
2. allkeys-lru:对所有key的近似LRU
3. volatile-lru:使用近似LRU算法淘汰设置了过期时间的key
4. allkeys-random:从所有key中随机淘汰一些key
5. volatile-random:对所有设置了过期时间的key随机淘汰
6. volatile-ttl:淘汰有效期最短的一部分key
Redis4.0 开始支持了 LFU 策略,和 LRU 类似,它分为两种:
7. volatile-lfu:使用LFU算法淘汰设置了过期时间的key
8. allkeys-lfu:从全部key中进行淘汰,使用LFU
最后
针对文章开始提到的问题,最好的解决办法是将使用内存量较大的业务 和 用户账号服务 使用的 redis 隔离开,这样就单个用户账号正常情况下是不会发生以上类似的问题了。