文章目录
Redis过期删除策略
🙎♂️面试官:如何设置key的过期时间?
🙋♂答:
expire <key> <n>
// 设置 key 在 n 秒后过期,比如 expire key 100 表示设置 key 在 100 秒后过期
set <key> <value> ex <n>
// 设置键值对的时候,同时指定过期时间(精确到秒)
TTL <key>
// 查看过期时间
PERSIST <key>
// 取消过期时间
🙎♂️面试官:什么是Redis过期删除策略?
🙋♂答:
过期删除策略就是删除过期键值对采用的方法。Redis采用惰性删除 + 定期删除两种策略。
- 惰性删除:就是在每次访问key的时候,都先对key的过期时间进行检查。
如果过期,就删除,返回null给客户端;没有过期就正常返回。 - 定期删除:会每隔10S 检查一次数据库,先从过期字典中随机抽取20个key,检查这20个key是否过期。
如果key的过期数量大于25%,继续重复进行检查;反之停止继续删除。
🙎♂️面试官:过期的key存放到哪里/如何判断key是否过期?
🙋♂答:
Redis中有一个过期字典,来保存有过期时间的key。所以在查找key时会先查找字典。
过期字典是一个哈希表,查找key的时间复杂度是O(1)。
查询key时,如果key在过期字典中,会将过期的时间与系统时间进行比较,如果比系统时间大,那就没有过期,否则判断已经过期。
- 过期字典的 key 是一个指针,指向某个键对象;
- 过期字典的 value 是一个 long long 类型的整数,这个整数保存了 key 的过期时间;
🙎♂️面试官:Redis具体是怎样实现过期删除策略的?
🙋♂答:
定期删除策略中:
- 定期删除时间在redis.conf中进行配置,hz默认就是10s。
- 随机抽查数量为20个key,是写死在代码里的。
内存淘汰策略
🙎♂️面试官:为什么有过期删除策略还要有内存淘汰机制?
🙋♂答:
不论是定期删除策略还是惰性删除机制,都不是一种完全精准的删除,还是会存在key没有被删除的场景,所以需要内存淘汰机制。
当Redis的运行内存超过了我们设置的阈值,就会触发内存淘汰机制。
🙎♂️面试官:常见的内存淘汰策略有几种?
🙋♂答:
Redis的内存淘汰机制有八种。
在进行数据淘汰的策略中,分为两种类型:
- 在设置了过期时间的数据中进行淘汰:
- volatile【短暂】-random:随机淘汰设置了过期时间的任意键值;
- volatile-ttl(Time-To-Live):优先淘汰更早过期的键值;
- volatile-lru(Least recently used,最近最少使用)(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,优先淘汰最久未用的键值;
- volatile-lfu(Least Frequently Used,最近最不常用)(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,优先淘汰最近最不常用的键值;
- 在所有数据范围内进行淘汰:
- allkeys-random:随机淘汰任意键值;
- allkeys-lru:淘汰整个键值中最久未用的键值;
- allkeys-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰整个键值中最不常用的键值。
noeviction(不进行驱逐)(Redis3.0之后,默认的内存淘汰策略) :表示不进行内存淘汰,而是不提供服务。直接返回错误。
🙎♂️面试官:Redis中,LRU 算法和 LFU 算法有什么区别?
🙋♂答:
传统 LRU 算法使用链表实现,链表中的元素按照操作顺序从前往后排列,最新操作的键会被移动到表头,当需要内存淘汰时,删除的链表尾部元素就代表最久未被使用的元素。
但是Redis 并没有使用这样的方式实现 LRU 算法,因为传统的 LRU 算法存在两个问题:
- 用链表管理Redis中的缓存数据,会有空间开销。
- 当有数据被访问时,需要在链表上把该数据移动到头端,如果有大量数据被访问,就会带来很多链表移动操作,会很耗时,进而会降低 Redis 缓存性能。
Redis中的实现如下,LRU(最近最少使用)算法根据时间淘汰,LFU(最近最不常用)算法根据使用次数淘汰。
- 在 LRU 算法中,Redis 对象头的 24 bits 的 lru 字段是用来记录 key 的访问时间戳,因此在 LRU 模式下,Redis可以根据对象头中的 lru 字段记录的值,来比较最后一次 key 的访问时间戳,从而淘汰最久未被使用的 key。
- Redis 实现的 LRU 算法的优点:
- 不用为所有的数据维护一个大链表,节省了空间占用;
- 不用在每次数据访问时都移动链表项,提升了缓存的性能;
- Redis 实现的 LRU 算法的优点:
但是 LRU 算法有一个问题,无法解决缓存污染问题,比如应用一次读取了大量的数据,而这些数据只会被读取这一次,那么这些数据会留存在 Redis 缓存中很长一段时间,造成缓存污染。LFU 内存淘汰算法是 Redis 4.0 之后新增内存淘汰策略,用来解决 LRU 算法的问题。
LFU 全称是 Least Frequently Used 翻译为最近最不常用的,LFU 算法是根据数据访问次数来淘汰数据的,它的核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。
所以, LFU 算法会记录每个数据的访问次数。当一个数据被再次访问时,就会增加该数据的访问次数。这样就解决了偶尔被访问一次之后,数据留存在缓存中很长一段时间的问题,相比于 LRU 算法也更合理一些。
- 在 LFU 算法中,Redis对象头的 24 bits 的 lru 字段被分成两段来存储,高 16bit 存储 ldt(Last Decrement Time),低 8bit 存储 logc(Logistic Counter)。
- ldt 是用来记录 key 的访问时间戳;
- logc 是用来记录 key 的访问频次,它的值越小表示使用频率越低,越容易淘汰,每个新加入的 key 的logc 初始值为 5。logc 并不是单纯的访问次数,而是访问频次(访问频率),因为 logc 会随时间推移而衰减的。