内存过期策略
首先,我们想想,假如我们自己来实现过期,有这样几种方法:
我们想到的方法 | 优点 | 缺点 |
给每个key都监控,一旦它的过期时间到了,立马删掉。 | 很及时 | 太费cpu了 |
每过一段时间,扫描所有的key,把已经过期的删掉 | 不那么费cpu | 不及时,且key太多的情况下也会耗cpu |
只有我去查这个key的时候,才去校验它是不是过期了,如果过期了我就删掉它,并且不返回 | 及时,不耗费cpu | 虽然不耗费cpu了,但是耗费内存啊!想想我要是几年不查一些key,那得完蛋 |
综上,没有完美的方法,redis使用的是上面结合的方法,叫做“定期过期+惰性过期”
redis使用的过期策略
定期随机抽取一批key,进行过期检查,如果过期则删掉。同时,正在查询的key也会校对过期时间。
细心的同学可能看到了,redis不是对所有的key进行抽取过期检查,而是对部分key进行抽取,原因是因为如果数据比较多的话抽取所有数据是一件繁琐的工作。
还有一个问题,由于redis是随机抽取一部分key进行过期校验的,那么就会存在一些key存在redis中一直没法淘汰掉,以至于内存有效使用率大大降低。内存淘汰策略就能够解决这个问题。
redis内存淘汰策略
redis内存淘汰策略分为以下六种:
- 当内存不足时,报错 —好像没有解决上面的问题。。。—
- 当内存不足时,随机淘汰一部分key —这不太好吧。。—
- 当内存不足时,淘汰最近最少使用的key —这个貌似可以,但是,如果我在redis中存了一些不过期的数据,你淘汰了也不太好吧。假如你在redis中数据都是可以过期的,倒没问题—
- 当内存不足时,从设置了过期时间的key里随机淘汰一些key —这个问题也不大,不过没啥优势啊—
- 当内存不足时,从设置了过期时间的key里淘汰快要过期的key —这。。都要过期了,你不抽取出来,过会儿也过期了,感觉做了无用功。。—
- 当内存不足时,从设置了过期时间的key里淘汰最近最少使用的key —这个最靠谱—
上面说的,淘汰最近最少使用的key,指的是LRU算法,LRU是Least Recently Used的缩写。那么如何手写一个LRU算法呢?
用LinkedHashMap实现LRU算法
我们可以用LinkedHashMap实现LRU算法。LinkedHashMap是一个带链表的HashMap,也就是key有顺序的HashMap,它能够把最近访问的key丢到链表一头,那么我们就可以从另一头把元素删掉来达到淘汰内存的目的,直接上代码:
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 使用linkedHashMap实现LRU算法
*
* @author Eren
* @date 2020/1/10 13:23
*/
public class LRUTest {
public static void main(String[] args) {
LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>(2, 0.75F, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > 2;
}
};
map.put(111, "111");
map.put(222, "222");
map.put(66, "66");
// {222=222, 66=66}
System.out.println(map);
String s = map.get(222);
// {66=66, 222=222}
System.out.println(map);
}
}