Appearance
Redis 过期策略以及内存淘汰机制
1. 数据为什么会过期?
首先,要明白 Redis 是用来做数据缓存的,不是用来做数据存储的(当然也可以当数据库用),所以数据时候过期的,过期的数据就不见了,过期主要有两种情况:
- 在设置缓存数据时制定了过期时间,这样到了过期时间数据就不见了;
- Redis 的数据是存放在内存中的,而内存是有限的,是不可能放过多数据的,比如只有 10G 的内存,想要向里面放入 20G 的数据,那么就注定会有 10G 的数据会丢失;
2. Redis 的过期策略是什么样的?
Redis 采用了 “定期删除 + 惰性删除” 的过期策略。
2.1. 定期删除
原理:定期删除指的是 Redis 默认每隔 100ms 就随机抽取一些设置了过期时间的 key,检测这些 key 是否过期,如果过期了就将其删掉。
为什么会选择一部分,而不是全部:因为如果这是 Redis 里面有大量的 key 都设置了过期时间,那么如果全部去检测一遍,CPU 负载就会很高,会浪费大量的时间在检测上面,甚至直接导致 Redis 挂掉。所有只会抽取一部分而不会全部检查。
出现问题:这样的话就会出现大量的已经过期的 key 并没有被删除,这就是为什么有时候大量的 key 明明已经过了失效时间,但是 Redis 的内存还是被大量占用的原因,为了解决这个问题,就需要惰性删除这个策略了。
2.2. 惰性删除
原理:惰性删除不在是 Redis 去主动删除,而是在你要获取某个 key 的时候,Redis 会先去检测一下这个 key 是否已经过期,如果没有过期则返回给你,如果已经过期了,那么 Redis 会删除这个 key,不会返回给你。
这样两种策略就保证了过期的 key 最终一定会被删除掉,但是这只是保证了最终一定会被删除,要是定时删除漏掉了大量过期的 key,而且我们也没有及时的去访问这些 key,那么这些 key 不就不会被删除了吗?不就会一直占着我们的内存吗?这样不还是会导致 Redis 内存耗尽吗?
由于存在这样的问题,所以 Redis 引入了内存淘汰机制来解决。
3. 内存淘汰机制
内存淘汰机制就保证了在 Redis 的内存占用过多的时候,去进行内存淘汰,也就是删除一部分 key,保证 Redis 的内存占用率不会过高,那么它会删除那些 key 呢?
Redis 提供了 6 中内存淘汰策略,我们可以去进行选择,六种策略如下:
- noeviction:当内存不足以容纳新写入数据时,新写入操作会报错,无法写入新数据,一般不采用;
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key,这个是最常用的;
- allkeys-random:当内存不足以容纳新写入的数据时,在键空间中,随机移除 key,一般也不使用;
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适);
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key;
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除;
4. 手写一个 LRU 算法
Java
// 基于 JavaLinkedHashMap 实现
public class LRUCache<K,V> extends LinkedHashMap<K,V>{
private final int CACHE_SIZE;
// 保存传递进来的最大数据量
public LRUCache(int cacheSize){
// 设置 hashmap 的初始大小,同时最后一个 true 指的是让 linkedhashmap 按照访问顺序来进行排序,
// 最近访问的放在头,最老访问的放在尾
super((int)Math.ceil(cacheSize/0.75)+1,0.75f,true);
CACHE_SIZE = CacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest){
// 当 map 中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
return size() > CACHE_SIZE;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18