面试官:Redis过期后key是怎么样清理的?

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 前言笔者一个同事面试某大厂时问到的一个问题,这里拿来讲讲:Redis过期后key是怎么样清理的?在Redis中,对于过期key的清理主要有惰性清除,定时清理,内存不够时清理三种方法,下面我们就来具体看看这三种清理方法。惰性清除在访问key时,如果发现key已经过期,那么会将key删除。定时清理Redis配置项hz定义了serverCron任务的执行周期,默认每次清理时间为25ms,每次清理会依次遍历所有DB,从db随机取出20个key,如果过期就删除,如果其中有5个key过期,那么就继续对这个db进行清理,否则开始清理下一个db。

前言

分割线.jpg

笔者一个同事面试某大厂时问到的一个问题,这里拿来讲讲:Redis过期后key是怎么样清理的?

在Redis中,对于过期key的清理主要有惰性清除,定时清理,内存不够时清理三种方法,下面我们就来具体看看这三种清理方法。


惰性清除


在访问key时,如果发现key已经过期,那么会将key删除。


定时清理


Redis配置项hz定义了serverCron任务的执行周期,默认每次清理时间为25ms,每次清理会依次遍历所有DB,从db随机取出20个key,如果过期就删除,如果其中有5个key过期,那么就继续对这个db进行清理,否则开始清理下一个db。


内存不够时清理


当执行写入命令时,如果发现内存不够,那么就会按照配置的淘汰策略清理内存,淘汰策略一般有6种,Redis4.0版本后又增加了2种,主要由分为三类

第一类 不处理,等报错(默认的配置)noeviction,发现内存不够时,不删除key,执行写入命令时直接返回错误信息。(Redis默认的配置就是noeviction)

第二类 从所有结果集中的key中挑选,进行淘汰allkeys-random 就是从所有的key中随机挑选key,进行淘汰allkeys-lru 就是从所有的key中挑选最近使用时间距离现在最远的key,进行淘汰allkeys-lfu 就是从所有的key中挑选使用频率最低的key,进行淘汰。(这是Redis 4.0版本后新增的策略)

第三类 从设置了过期时间的key中挑选,进行淘汰这种就是从设置了expires过期时间的结果集中选出一部分key淘汰,挑选的算法有:volatile-random 从设置了过期时间的结果集中随机挑选key删除。volatile-lru 从设置了过期时间的结果集中挑选上次使用时间距离现在最久的key开始删除volatile-ttl 从设置了过期时间的结果集中挑选可存活时间最短的key开始删除(也就是从哪些快要过期的key中先删除)volatile-lfu 从过期时间的结果集中选择使用频率最低的key开始删除(这是Redis 4.0版本后新增的策略)


LRU算法


LRU算法的设计原则是如果一个数据近期没有被访问到,那么之后一段时间都不会被访问到。所以当元素个数达到限制的值时,优先移除距离上次使用时间最久的元素。

可以使用双向链表Node+HashMap来实现,每次访问元素后,将元素移动到链表头部,当元素满了时,将链表尾部的元素移除,HashMap主要用于根据key获得Node以及添加时判断节点是否已存在和删除时快速找到节点。

PS:使用单向链表能不能实现呢,也可以,单向链表的节点虽然获取不到pre节点的信息,但是可以将下一个节点的key和value设置在当前节点上,然后把当前节点的next指针指向下下个节点,这样相当于把下一个节点删除了


//双向链表
public static class ListNode {       
String key;//这里存储key便于元素满时,删除尾节点时可以快速从HashMap删除键值对        Integer value;       
ListNode pre = null;       
ListNodenext= null;
ListNode(String key, Integer value) {           
this.key = key;           
this.value = value;       
}   
}   
istNode head;   
ListNode last;   
int limit=4;
HashMap hashMap = new HashMap();   
public void add(String key, Integer val) {       
ListNode existNode = hashMap.get(key);
if(existNode!=null) {
//从链表中删除这个元素           
ListNode pre = existNode.pre;           
ListNodenext= existNode.next;
if(pre!=null) {
pre.next=next;
}if(next!=null) {
next.pre = pre;
}           
//更新尾节点
if(last==existNode) {
last = existNode.pre;
}           
//移动到最前面           
head.pre = existNode;           
existNode.next= head;
head = existNode;           
//更新值           
existNode.value = val;       
}else{
//达到限制,先删除尾节点
if(hashMap.size() == limit) {
ListNode deleteNode = last;               
hashMap.remove(deleteNode.key);
//正是因为需要删除,所以才需要每个ListNode保存key               
last = deleteNode.pre;               
deleteNode.pre = null;               
last.next= null;
}           
ListNode node = new ListNode(key,val);           
hashMap.put(key,node);if(head==null) {
head = node;               
last = node;           
}else{
//插入头结点               
node.next= head;
head.pre = node;               
head = node;           
}       
}   
}   
public ListNode get(String key) {
returnhashMap.get(key);
}   
public voidremove(String key) {
ListNode deleteNode = hashMap.get(key);       
ListNode preNode = deleteNode.pre;       
ListNode nextNode = deleteNode.next;
if(preNode!=null) {
preNode.next= nextNode;
}if(nextNode!=null) {
nextNode.pre = preNode;       
}if(head==deleteNode) {
head = nextNode;       
}if(last == deleteNode) {
last = preNode;        }       
hashMap.remove(key);
}


最后


LFU算法的设计原则时,如果一个数据在最近一段时间被访问的时次数越多,那么之后被访问的概率会越大,基本实现是每个数据都有一个引用计数,每次数据被访问后,引用计数加1,需要淘汰数据时,淘汰引用计数最小的数据。在Redis的实现中,每次key被访问后,引用计数是加一个介于0到1之间的数p,并且访问越频繁p值越大,而且在一定的时间间隔内,如果key没有被访问,引用计数会减少。


image.png

相关文章
|
1月前
|
存储 缓存 NoSQL
Redis常见面试题全解析
Redis面试高频考点全解析:从过期删除、内存淘汰策略,到缓存雪崩、击穿、穿透及BigKey问题,深入原理与实战解决方案,助你轻松应对技术挑战,提升系统性能与稳定性。(238字)
|
6月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
4月前
|
存储 NoSQL 定位技术
Redis数据类型面试给分情况
Redis常见数据类型包括:string、hash、list、set、zset(有序集合)。此外还包含高级结构如bitmap、hyperloglog、geo。不同场景可选用合适类型,如库存用string,对象存hash,列表用list,去重场景用set,排行用zset,签到用bitmap,统计访问量用hyperloglog,地理位置用geo。
122 5
|
5月前
|
缓存 NoSQL Java
Java Redis 面试题集锦 常见高频面试题目及解析
本文总结了Redis在Java中的核心面试题,包括数据类型操作、单线程高性能原理、键过期策略及分布式锁实现等关键内容。通过Jedis代码示例展示了String、List等数据类型的操作方法,讲解了惰性删除和定期删除相结合的过期策略,并提供了Spring Boot配置Redis过期时间的方案。文章还探讨了缓存穿透、雪崩等问题解决方案,以及基于Redis的分布式锁实现,帮助开发者全面掌握Redis在Java应用中的实践要点。
308 6
|
8月前
|
缓存 NoSQL Java
Redis应用—6.热key探测设计与实践
热key问题在高并发系统中可能导致数据层和服务层的严重瓶颈,如Redis集群瘫痪和用户体验下降。为解决此问题,京东开发了JdHotkey热key探测框架,具备实时性、准确性、集群一致性和高性能等特点。该框架由etcd集群、Client端jar包、Worker端集群和Dashboard控制台组成,通过分布式计算快速识别热key并推送至应用内存,有效减轻数据层负载,提升服务性能。JdHotkey适用于多种场景,安装部署简便,支持毫秒级热key探测和集群一致性维护。
430 61
Redis应用—6.热key探测设计与实践
|
5月前
|
NoSQL 测试技术 Redis
Redis批量删除Key的三种方式
Redis批量删除Key是优化数据库性能的重要操作,本文介绍三种高效方法:1) 使用通配符匹配(KEYS/SCAN+DEL),适合不同数据规模;2) Lua脚本实现原子化删除,适用于需要事务保障的场景;3) 管道批量处理提升效率。根据实际需求选择合适方案,注意操作不可逆,建议先备份数据,避免内存溢出或阻塞。
|
7月前
|
存储 NoSQL Redis
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
|
10月前
|
NoSQL API Redis
在C程序中实现类似Redis的SCAN机制的LevelDB大规模key分批扫描
通过上述步骤,可以在C程序中实现类似Redis的SCAN机制的LevelDB大规模key分批扫描。利用LevelDB的迭代器,可以高效地遍历和处理数据库中的大量键值对。该实现方法不仅简单易懂,还具有良好的性能和扩展性,希望能为您的开发工作提供实用的指导和帮助。
161 7
|
10月前
|
存储 缓存 NoSQL
Redis 面试题
Redis 基础面试题
240 1
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?