目录
-
- 一、过期策略
-
- 1.1、定时删除
-
- 1.1.1、过期
- [1.1.2、过期的 key 集合](#1.1.2、过期的 key 集合)
- 1.1.3、定时扫描策略
- [1.1.4、 Redis 中所有的 key 在同一时间过期了,会出现怎样的结果](#1.1.4、 Redis 中所有的 key 在同一时间过期了,会出现怎样的结果)
- 1.1.5、从库的过期策略
- 1.2、惰性删除
- 1.3、定时删除和惰性删除的总结
- 二、缓存淘汰算法
-
- 2.1、缓存淘汰算法概述
- 2.2、缓存淘汰算法机制
- 2.3、缓存淘汰算法总结
- [2.4、LRU 算法概述](#2.4、LRU 算法概述)
- [2.5、近似 LRU 算法概述](#2.5、近似 LRU 算法概述)
一、过期策略
1.1、定时删除
1.1.1、过期
- Redis 所有的数据结构都可以设置过期时间,时间一到,就会自动删除。但是会不会因为同一时间太多的key 过期,以至于忙不过来。同时因为Redis 是单线程的,删除的时间也会占用线程的处理时间,如果删除的大过于繁忙,会不会导致线上读写指令出现卡顿。
1.1.2、过期的 key 集合
- redis 会将每个设置了过期时间的key 放入到一个独立的字典中 ,以后会定时遍历 这个字典 来删除到期的 key。除了定时遍历之外,它还会使用惰性策略来删除过期的 key,所谓惰性策略就是在客户端访问这个 key 的时候,redis 对 key 的过期时间进行检查,如果过期了就立即删除。定时删除是集中处理,惰性删除是零散处理。
1.1.3、定时扫描策略
- Redis 默认会每秒进行十次过期扫描 ,过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。
(1)、从过期字典中随机 20 个 key;
(2)、删除这 20 个 key 中已经过期的 key;
(3)、 如果过期的 key 比率超过 1/4,那就重复步骤(1);
1.1.4、 Redis 中所有的 key 在同一时间过期了,会出现怎样的结果
- Redis 会持续扫描过期字典(循环多次),直到过期字典中过期的key 变得稀疏,才会停止(循环次数明显下降)。这就会导致线上读写请求出现明显的卡顿现象。导致这种卡顿的另外一种原因是内存管理器需要频繁回收内存页,这也会产生一定的 CPU 消耗。
- 所以业务开发人员一定要注意过期时间,如果有大批量的 key 过期,要给过期时间设置一个随机范围,而不能全部在同一时间过期。
1.1.5、从库的过期策略
- 从库不会进行过期扫描 ,从库对过期的处理是被动的。主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指今来删除过期的 key。
- 因为指令同步是异步进行的,所以主库过期的key 的 del 指令没有及时同步到从库的话,会出现主从数据的不一致,主库没有的数据在从库里还存在,比如上-节的集群环境分布式锁的算法漏洞就是因为这个同步延迟产生的。
1.2、惰性删除
- 所谓惰性策略就是在客户端访问这个kev的时候,redis对key的过期时间进行检查,如果过期了就立即删除,不会给你返回任何东西。
- 定期删除可能会导致很多过期kev到了时间并没有被删除。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,即当你主动去查过期的key时,如果发现key过期了,就立即进行删除,不返回任何东西
1.3、定时删除和惰性删除的总结
- 定期删除是集中处理,惰性删除是零散处理。
二、缓存淘汰算法
2.1、缓存淘汰算法概述
2.1.1、概述
- 当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换(swap)。交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样龟速的存取效率基本上等于不可用。
2.1.2、maxmemory
- 在生产环境中我们是不允许 Redis 出现交换行为的,为了限制最大使用内存,Redis 提供了配置参数maxmemory 来限制内存超出期望大小。
- 当实际内存超出 maxmemory 时,Redis 提供了几种可选策略(maxmemory-policy) 来让用户自己决定该如何腾出新的空间以继续提供读写服务。
2.2、缓存淘汰算法机制
2.2.1、Noeviction
- noeviction 不会继续服务写请求,(DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略.
2.2.2、volatile-Iru
- volatile-lru 尝试淘汰设置了过期时间的key,最少使用的 key 优先被淘汰。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。
2.2.3、volatile-ttl
- voatie-ttl 跟上面一样,除了淘汰的策略不是 LRU,而是key 的剩余寿命ttl的值,ttl 越小越优先被淘汰
2.2.4、volatile-random
- volatile-random 跟上面一样,不过淘汰的 key 是过期 key 集合中随机的 key。
2.2.5、allkeys-lru
- allkeys-lru 区别于volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不只是过期的 key 集合。这意味着没有设置过期时间的 key 也会被淘汰。
2.2.6、allkeys-random
- allkeys-random跟上面一样,不过淘汰的策略是随机的 key。
2.3、缓存淘汰算法总结
- volatile-xxx 策略只会针对带过期时间的key 进行淘汰。
- allkeys-xxx 策略会对所有的 key 进行淘汰。
- 如果你只是拿 Redis 做缓存,那应该使用 allkeys-xxx,客户端写缓存时不必携带过期时间。
- 如果你还想同时使用 Redis 的持久化功能,那就使用 volatile-xxx 策略,这样可以保留没有设置过期时间的 key,它们是永久的 key 不会被LRU 算法淘汰。
2.4、LRU 算法概述
- 实现 LRU 算法除了需要key/value 字典外,还需要附加一个链表,链表中的元素按照一定的顺序进行排列。当空间满的时候,会踢掉链表尾部的元素。当字典的某个元素被访问时,它在链表中的位置会被移动到表头。所以销表的元素排列顺序就是元素最近被访问的时间顺序。
- 位于链表尾部的元素就是不被重用的元素,所以会被踢掉。位于表头的元素就是最近刚刚被人用过的元素,所以暂时不会被踢。
2.5、近似 LRU 算法概述
- Redis 使用的是一种近似 LRU 算法,它跟 LRU 算法还不太一样。之所以不使用 LRU 算法,是因为需要消耗大量的额外的内存,需要对现有的数据结构进行较大的改造。
- 近似LRU 算法则很简单,在现有数据结构的基础上使用随机采样法来淘汰元素,能达到和 LRU 算法非常近似的效果。Redis 为实现近似LRU 算法,它给每个 key 增加了一个额外的小字段,这个字段的长度是 24 个 bit,也就是最后一次被访问的时间戳。
- 当 Redis 执行写操作时,发现内存超出maxmemory,就会执行一次 LRU 淘汰算法。这个算法也很简单,就是随机采样出 5可以配置maxmemory-samples) 个 key,然后淘汰掉最旧的 key,如果淘汰后内存还是超出maxmemory,那就继续随机采样淘汰,直到内存低于 maxmemory 为止。