Redis过期删除策略和内存淘汰策略

1. 过期删除策略

Redis可以用使用expire指令设置过期时间,在Redis内部,每当我们设置一个键的过期时间时,Redis就会将该键带上过期时间存放到一个过期字典中。当我们查询一个键时,Redis便首先检查该键是否存在过期字典中,如果存在,那就获取其过期时间。然后将过期时间和当前系统时间进行比对,比系统时间大,那就没有过期;反之判定该键过期。

1.1 过期数据处理方式

那对于过期数据,一般有三种方式进行处理:

  1. 定时删除
    在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
    • 优点:定时删除对内存是最友好的,能够保存内存的key一旦过期就能立即从内存中删除。
    • 缺点:对CPU最不友好,在过期键比较多的时候,删除过期键会占用一部分 CPU 时间,对服务器的响应时间和吞吐量造成影响。
  2. 惰性删除
    设置该key 过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
    • 优点:对 CPU友好,我们只会在使用该键时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。
    • 缺点:对内存不友好,如果一个键已经过期,但是一直没有使用,那么该键就会一直存在内存中,如果数据库中有很多这种使用不到的过期键,这些键便永远不会被删除,内存永远不会释放。从而造成内存泄漏。
  3. 定期删除
    每隔一段时间,我们就对一些key进行检查,删除里面过期的key。
    • 优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。
    • 缺点:难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时得到释放。另外最重要的是,在获取某个键时,如果某个键的过期时间已经到了,但是还没执行定期删除,那么就会返回这个键的值,这是业务不能忍受的错误。

1.2 Redis过期策略

Redis的过期删除策略:惰性删除定期删除两种策略配合使用。

  • 惰性删除:Redis的惰性删除策略由 db.c/expireIfNeeded 函数实现,所有键读写命令执行之前都会调用 expireIfNeeded 函数对其进行检查,如果过期,则删除该键,然后执行键不存在的操作;未过期则不作操作,继续执行原有的命令
  • 定期删除:由redis.c/activeExpireCycle 函数实现,函数以一定的频率运行,每次运行时,都从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中的过期键。
    注意:并不是一次运行就检查所有的库,所有的键,而是随机检查一定数量的键。
    定期删除函数的运行频率,在Redis2.6版本中,规定每秒运行10次,大概100ms运行一次。在Redis2.8版本后,可以通过修改配置文件redis.confhz 选项来调整这个次数:hz值越大,对cpu压力越大

1.3 Redis中key过期监听事件

spring-boot-starter-data-redis包中提供了监听过期的类,对于key过期,需要得到通知,做业务处理的,可以做此监听。

    1. 修改配置文件,开启监听事件
notify-keyspace-events "Ex" # 值填Ex
    1. springboot配置类

springboot整合Redis参考,SpringBoot整合Redis - (jianshu.com)
在整合Redis的基础上,在新加监听配置

监听配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

@Configuration
public class RedisListenerConfig {
    
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }

}

监听类

import java.nio.charset.StandardCharsets;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;

@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    
    protected final Logger log = LoggerFactory.getLogger(this.getClass());
    
    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }
    
    
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String channel = new String(message.getChannel(),StandardCharsets.UTF_8);
        //过期的key
        String expireKey = new String(message.getBody(), StandardCharsets.UTF_8);
        // TODO
        log.info("\r\n ====>>>> redis key 过期:key={}, pattern={}, channel={}", expireKey, new String(pattern), channel);
    }

}
    1. 测试一把


      写入数据

      监听记录

2. 内存淘汰策略

将Redis用作缓存时,如果内存空间用满,就会自动驱逐老的数据。

2.1 策略

Redis中有6种淘汰策略:

  • noeviction:当内存使用达到阈值的时候,执行命令直接报错,默认
  • allkeys-lru:在所有的key中,优先移除最近未使用的key。(推荐)
  • volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。
  • allkeys-random:在所有的key中,随机移除某个key。
  • volatile-random:在设置了过期时间的键空间中,随机移除某个key。
  • volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除

2.2 配置文件

redis.conf文件中配置策略,有2个地方:

    1. 内存大小
      maxmemory 参数配置Redis 内存大小,通常会设定其为物理内存的四分之三
    1. 策略设置
      maxmemory-policy 参数

你可能感兴趣的:(Redis过期删除策略和内存淘汰策略)