总结不易,如果对你有帮助,请点赞关注支持一下
微信搜索程序dunk,关注公众号,获取博客源码、数据结构与算法笔记(超级全)、大厂面试、笔试题
Redis过期键的删除策略
对于过期键一般的三种删除策略
- 定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作
- 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,那就返回该键
- 定期删除:每过一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于删除多少过期的键,以及检查多少个数据库,则由算法决定
三种策略优缺点
- 定时删除策略
- 对内存是最友好的:通过使用定时器,定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占的内存;
- 但另一方面,定时删除策略的缺点是,它对CPU是最不友好的:在过期键比较多的情况下,将CPU时间用在删除和当前任务无关的过期键上,无疑对服务器的响应时间和吞吐量造成影响
- 惰性删除策略
- 惰性删除策略对CPU时间来说是友好的:程序只会在取出键时才对键进行过期检查是,这可以保证删除过期键的操作只会在非做不可的情况下进行
- 惰性删除的缺点:它对内存是最不友好的:如果一个键已经过期,而这个键又仍然保留在数据库中,那么只要这个过期键不被删除,他所占用的内存就不会被释放掉
- 定时删除占用太多的CPU时间,影响服务器的响应时间和吞吐量;惰性删除浪费太多的内存,有内存泄漏的危险。定期删除策略是前两种策略的一种整合折中
- 定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响
- 通过定期删除过期键,定期删除策略有效地减少了因为过期键带来的内存浪费
- 定期删除策略的难点是确定删除操作的执行时长和频率
问题来了:定期没删,也没查询,该怎么办?
内存淘汰机制
-
noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
-
allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
-
volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
-
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
-
volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
-
volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
如果没有键满足回收的前提条件的话,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了。
LRU(最近最少使用算法)
/**
* @author :zsy
* @date :Created 2021/7/25 12:41
* @description:https://www.nowcoder.com/practice/e3769a5f49894d49b871c09cadd13a61?tpId=190&&tqId=35214&rp=1&ru=/activity/oj&qru=/ta/job-code-high-rd/question-ranking
*/
public class NC93设计LRU缓存结构 {
@Test
public void test() {
}
public class Solution {
/**
* lru design
* @param operators int整型二维数组 the ops
* @param k int整型 the k
* @return int整型一维数组
*/
public int[] LRU (int[][] operators, int k) {
// write code here
LRUCache lruCache = new LRUCache(k);
ArrayList<Integer> res = new ArrayList<>();
for (int[] operator : operators) {
if (operator[0] == 1) {
lruCache.put(operator[1], operator[2]);
} else {
res.add(lruCache.get(operator[1]));
}
}
int[] ans = new int[res.size()];
for (int i = 0; i < res.size(); i++) {
ans[i] = res.get(i);
}
return ans;
}
class LRUCache {
int capacity;
Map<Integer, Node> map;
DoubleList list;
public LRUCache (int cap) {
this.capacity = cap;
this.map = new HashMap<>();
this.list = new DoubleList();
}
/**
* 添加缓存
* 如果map中包含当前key,则修改当前的val值,并将节点添加到链表头部
* 否则
* 如果双端链表的长度小于cap,直接添加到链表头部
* 否则,移除链表最后一个节点,添加到链表头部,删除map中的映射
* 最后,添加到map中
* @param key
* @param val
*/
public void put(int key, int val) {
Node cur = new Node(key, val);
if(map.containsKey(key)) {