redis过期策略以及内存淘汰机制(理论+配置)
一、redis的过期策略:
redis的过期策略是:定期删除+惰性删除
redis在存储数据时,可能会设置过期时间,而所谓的定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key进行检查,如果过期了就会删除。
至于为啥是每隔100ms随机抽取一些数据进行检查而不是全部检查,这就与cpu负载有关了,如redis中的数据十分庞大,并且全部都设置了过期时间,依次全部检查并且进行删除的话负载太高,影响性能。
但是,由于是随机抽取的key进行检查进行删除,那么很多的key可能会到了过期时间了还没进行删除,那么怎么办呢?这时候,惰性删除就会发挥作用了,所谓的惰性删除,就是在读取某个key的时候,redis会先检查一个该key是否过期,如果过期了,就会在此时删除,然后不会给你返回任何东西。
但是此时就会产生另外一个问题,假如一些key设置了过期时间,而定期删除的随机抽取没有选中这些key,而恰好也没有人去获取这些key,惰性删除也发挥不了作用了,那么这些数据就会越积累越多,redis一般作为缓存的,是基于内存的,这些数据越来越多的时候回导致内存耗尽,影响性能,这时候应该怎么办呢?这时候,另一个重量型的武器就要发挥作用了,那就是:内存淘汰机制。
二、内存淘汰机制:
redis 内存淘汰机制(内存淘汰策略)有以下几个:
• noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
• allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
• allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
• volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
• volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
• volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。
但是理论性的东西我们明白了,那么该如何配置内存淘汰策略呢?
三、内存淘汰策略的配置
我们通过配置redis.conf中的maxmemory这个值来开启内存淘汰功能。
# maxmemory
值得注意的是,maxmemory为0的时候表示我们对Redis的内存使用没有限制。
根据应用场景,选择淘汰策略
# maxmemory-policy noeviction
我们来了解下,内存淘汰的过程
首先,客户端发起了需要申请更多内存的命令(如set)。
然后,Redis检查内存使用情况,如果已使用的内存大于maxmemory则开始根据用户配置的不同淘汰策略来淘汰内存(key),从而换取一定的内存。
最后,如果上面都没问题,则这个命令执行成功。
动态改配置命令
此外,redis支持动态改配置,无需重启。
设置最大内存
config set maxmemory 100000
设置淘汰策略
config set maxmemory-policy noeviction
如何选择淘汰策略
下面看看几种策略的适用场景
allkeys-lru:如果我们的应用对缓存的访问符合幂律分布,也就是存在相对热点数据,或者我们不太清楚我们应用的缓存访问分布状况,我们可以选择allkeys-lru策略。
allkeys-random:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略。
volatile-ttl:这种策略使得我们可以向Redis提示哪些key更适合被eviction。
另外,volatile-lru策略和volatile-random策略适合我们将一个Redis实例既应用于缓存和又应用于持久化存储的时候,然而我们也可以通过使用两个Redis实例来达到相同的效果,值得一提的是将key设置过期时间实际上会消耗更多的内存,因此我们建议使用allkeys-lru策略从而更有效率的使用内存。
四、设置过期时间
redis有四种命令可以用于设置键的生存时间和过期时间:
EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒
PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒
EXPIREAT <KEY> <timestamp> :将键的过期时间设为 timestamp 所指定的秒数时间戳
PEXPIREAT <KEY> <timestamp>: 将键的过期时间设为 timestamp 所指定的毫秒数时间戳.
当然,平时我们也可以使用java代码的方式进行过期时间的设置,一些工具类可以直接满足要求,在这里,我就提供一个工具类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit; /**
* redis的工具类
* @author zangchuanlei
* @date 2019.09.18
*/
@Component
public final class RedisUtil { @Autowired
private RedisTemplate<String,Object> redisTemplate; /**
* 指定缓存失效的时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key,long time){
try {
if(time > 0){
redisTemplate.expire(key,time,TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 根据key获取过期时间
* @param key 键 不能为null
* @return 时间(秒)返回0代表为永久有效
*/
public long getExpire(String key){
return redisTemplate.getExpire(key,TimeUnit.SECONDS);
} /**
* 判断key是否存在
* @param key 键
* @return true 存在 false 不存在
*/
public boolean hasKey(String key){
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 删除缓存
* @param key 可以传一个值或者多个值
*/
public void del(String... key){
if(key != null && key.length > 0){
if(key.length == 1){
redisTemplate.delete(key[0]);
}else{
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
} //=========================String======================
/**
*普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key){
return key == null ? null : redisTemplate.opsForValue().get(key);
} /**
* 普通缓存的放入
* @param key
* @param value
* @return
*/
public boolean set(String key, Object value){
try {
redisTemplate.opsForValue().set(key,value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0,如果time要是小于0,将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key,Object value,long time){
try {
if(time > 0){
redisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);
}else{
set(key,value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key,long delta){
if(delta < 0){
throw new RuntimeException("增强因子必须大于0");
}
return redisTemplate.opsForValue().increment(key,delta);
} /**
* 递减
* @param key 键
* @param delta 要减少几(大于0)
* @return
*/
public long decr(String key,long delta){
if(delta < 0){
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key,-delta);
} /**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key,String item){
return redisTemplate.opsForHash().get(key,item);
} /**
* 获取hashKey对应的所有的键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object,Object> hmget(String key){
return redisTemplate.opsForHash().entries(key);
} /**
* hashSet
* @param key 键
* @param map 对应多个键值
* @return true成功 false 失败
*/
public boolean hmset(String key,Map<String,Object> map){
try {
redisTemplate.opsForHash().putAll(key,map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* HashSet并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间秒
* @return true 成功 false 失败
*/
public boolean hmset(String key,Map<String,Object> map,long time){
try {
redisTemplate.opsForHash().putAll(key,map);
if(time > 0){
expire(key,time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true成功 false失败
*/
public boolean hset(String key,String item,Object value){
try {
redisTemplate.opsForHash().put(key,item,value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已经存在的hash表有时间,这里将会替换原有的时间
* @return true成功 false 失败
*/
public boolean hset(String key,String item,Object value,long time){
try {
redisTemplate.opsForHash().put(key,item,value);
if(time > 0){
expire(key,time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使用多个 但不能为null
*/
public void hdel(String key,Object... item){
redisTemplate.opsForHash().delete(key,item);
} /**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false 不存在
*/
public boolean hHasKey(String key,String item){
return redisTemplate.opsForHash().hasKey(key,item);
} /**
* hash递增 如果不存在,就会创建一个 把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key,String item,double by){
return redisTemplate.opsForHash().increment(key,item,by);
} /**
* hash递减 如果不存在,就会创建一个 把新增后的值返回
* @param key 键
* @param item 项
* @param by 要减少几(大于0)
* @return
*/
public double hdecr(String key,String item,double by){
return redisTemplate.opsForHash().increment(key,item,-by);
} //==========================set==================== /**
* 根据key获取set中的所有值
* @param key 键
* @return
*/
public Set<Object> sGet(String key){
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 根据value从一个set中查询,是否存在
* @param key 键
* @param value 值
* @return true存在 false不存在
*/
public boolean sHashKey(String key,Object value){
try {
return redisTemplate.opsForSet().isMember(key,value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key,Object... values){
try {
return redisTemplate.opsForSet().add(key,values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} /**
* 将set数据放入缓存并设置失效时间
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key,long time,Object... values){
try {
Long count = redisTemplate.opsForSet().add(key,values);
if(time > 0){
expire(key,time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} /**
* 获取set缓存的长度
* @param key 键
* @return
*/
public long sGetSetSize(String key){
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} /**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key,Object... values){
try {
Long count = redisTemplate.opsForSet().remove(key,values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} //===================list==================== /**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0到-1代表所有值
* @return
*/
public List<Object> lGet(String key,long start,long end){
try {
return redisTemplate.opsForList().range(key,start,end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 获取list缓存的长度
* @param key 键
* @return
*/
public long lGetListSize(String key){
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} /**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=0, 0表头,1 第二个元素 ;
* 索引index<0时,-1,表尾,-2倒数第二个元素 以此类推
* @return
*/
public Object iGetIndex(String key,long index){
try {
return redisTemplate.opsForList().index(key,index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key,Object value){
try {
redisTemplate.opsForList().rightPush(key,value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key,Object value,long time){
try {
redisTemplate.opsForList().rightPush(key,value);
if(time > 0){
expire(key,time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key,List<Object> value){
try {
redisTemplate.opsForList().rightPushAll(key,value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key,List<Object> value,long time){
try {
redisTemplate.opsForList().rightPushAll(key,value);
if(time > 0){
expire(key,time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key,long index,Object value){
try {
redisTemplate.opsForList().set(key,index,value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key,long count,Object value){
try {
Long remove = redisTemplate.opsForList().remove(key,count,value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
} }
参考原文链接:https://blog.csdn.net/baidu_26954625/article/details/90648597
redis过期策略以及内存淘汰机制(理论+配置)的更多相关文章
- Redis(六)--- Redis过期策略与内存淘汰机制
1.简述 关于Redis键的过期策略,首先要了解两种时间的区别,生存时间和过期时间: 生存时间:一段时长,如30秒.6000毫秒,设置键的生存时间就是设置这个键可以存在多长时间,命令有两个 expir ...
- redis过期策略和内存淘汰机制
目录 常见的删除策略 redis使用的过期策略:定期删除+惰性删除 定期删除 惰性删除 为什么要采用定期删除+惰性删除2种策略呢? redis内存淘汰机制 常见的删除策略 1.定时删除:在设置键的过期 ...
- redis过期策略与内存淘汰机制分析
过期策略: 我们在set key时,可以给一个expire time,就是过期时间 这段过期时间以后,redis对key删除使用:定期删除+惰性删除 定期删除指redis默认在100ms内随机抽取一些 ...
- redis过期策略、内存淘汰策略、持久化方式、主从复制
原文链接:https://blog.csdn.net/a745233700/article/details/85413179 一.Redis的过期策略以及内存淘汰策略:1.过期策略:定期删除+惰性删除 ...
- Redis的过期策略和内存淘汰机制
过期策略 我们set key的时候,都可以给一个expire time,就是过期时间,指定这个key比如说只能存活1个小时,我们自己可以指定缓存到期就失效. 如果假设你设置一个一批key只能存活1个小 ...
- redis的过期策略以及内存淘汰机制
redis采用的是定期删除+惰性删除策略. 为什么不用定时删除策略? 定时删除,用一个定时器来负责监视key,过期则自动删除.虽然内存及时释放,但是十分消耗CPU资源.在大并发请求下,CPU要将时间应 ...
- 关于redis的几件小事(四)redis的过期策略以及内存淘汰机制
1.数据为什么会过期? 首先,要明白redis是用来做数据缓存的,不是用来做数据存储的(当然也可以当数据库用),所以数据时候过期的,过期的数据就不见了,过期主要有两种情况, ①在设置缓存数据时制定了过 ...
- Redis的过期删除策略(和内存淘汰机制)-转
版权声明:本文为CSDN博主「奥修诺斯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/qq_39944869/ ...
- Redis 中的过期删除策略和内存淘汰机制
Redis 中 key 的过期删除策略 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 ...
随机推荐
- 数据中台实战(一):以B2B电商亿订为例,谈谈产品经理视角下的数据埋点
本文以B2B电商产品“亿订”为实例,与大家一同谈谈数据中台的数据埋点. 笔者所在公司为富力环球商品贸易港,是富力集团旗下汇聚原创设计师品牌及时尚买手/采购商两大社群,通过亿订B2B电商.RFSHOWR ...
- 焦大:SEO重思录(上)收录量和收录率的重新定位
http://www.wocaoseo.com/thread-198-1-1.html 前一段焦大在seo前线就看到有人问为何我收录量很大但是流量很低呢?有时候几百万的收录量但是流量却只有1000不到 ...
- 《Head First 设计模式》:模板方法模式
正文 一.定义 模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤. 要点: 模板方法定义了一个算法的步骤,每 ...
- 2020.08.23 瞎扯周记之论短暂假期(QAQ)内要不要睡午觉
蒟蒻的假期都是很短暂的嘛 作为一只合格的蒟蒻 假期自然是很短暂的QAQ 只有短短的26h93360s(手动微笑) 总体来讲 假期只有两件事要干: 1.满足人体自身需求 2.满足作业需求 2.5.摸鱼 ...
- vue学习笔记一
vue 颠覆式的开发方式 解疑 为什么要学习vue? 降低项目的复杂度 就业需要 vue难不难? 特别简单 相比于React,vue有什么优势? 前端三大框架:Angular.React.Vue Vu ...
- 14 el-dialog 基本结构
1 dialogVisible父组件提供,:visible.sync直接修改父组件的dialogVisible,会报错,需要加上before-close属性 <template> < ...
- 使用java爬虫从雪球网下载股票数据
雪球网也是采用Ajax方式展示数据,我依然采用开发者工具查看其访问地址和返回数据. 访问使用到的库是jsoup,解析返回的json用的类库是jackson,二者的依赖是: <!-- jsoup ...
- SpringBoot程序启动时在Oracle数据库中建表充值
例子工程下载链接:https://files.cnblogs.com/files/xiandedanteng/gatling20200428-1.zip 需求:在工程启动时在Oracle数据库中建表. ...
- URL 传参转义 (特殊符号转义)
问题: url参数中 存在+,空格,/,?,%,#,&,=等特殊符号的时候,可能在服务器端无法获得正确的参数值,如何是好? 解决办法:将这些字符转化成服务器可以识别的字符,对应关系如下:URL ...
- 关于HTML基本标签,了解一下!
目录 1 前言 2 正文 2.1 HTML文档结构 2.1.1 html标签 2.1.2 head标签 2.1.3 title标签 2.1.4 body标签 2.2 HTML常用标签 2.2.1 换行 ...