最近在做用户体系模块,其中涉及到验证码存储问题,我们目前采用的是redis分片进行缓存处理。其中搭建及配置redis很简单,此处就不再重复。下午遇到使用setex(key, seconds, value)方法,但是缓存数据未过期。然后查看了一下ShardedJedis的setex(key, seconds, value)源码,
public String setex(String key, int seconds, String value) {
Jedis j = getShard(key);
return j.setex(key, seconds, value);
}
为什么会出现缓存数据过期未清空的情况呢?
经过分析,查看到原来做redis分片的两台服务器,我本地是配置的是从库(原来考虑的是做redis的一主二从).虽然redis.conf的 slave-read-only no
,但是配置文件没有进行修改
# Master服务器IP port
slaveof 127.0.0.1 6379
这样,缓存数据成功,但是过期时间没有生效。然后修改redis.conf中slave的配置,OK了。所以果断去查看了redis主键删除策略及实现机制。
redis三种不同的删除策略
1,定时删除
定时删除对内存是最友好的,可以保证过期键会被尽快的删除掉,内存被尽早的释放出来,虽然对内存比较友好,但是服务器需要创建大量的定时器,来实现定时删除,如果内存现在并不缺少,有空闲的内存,而这个时候有大量的命令请求在等待服务器处理,那么服务器也应该优先把CUP时间给处理客户端请求上,而不是删除过期键上。
所以定时删除不是一个完美的策略。
2,懒惰删除
客户端现在取这个数据,这个key已经过期了,明显不能返回给客户端,这个时候也就是使用懒惰删除了。可以看出,这个策略对内存是极不好的,但是CUP时间是最友好的,不会轻易动用CUP时间去删除过期键。
3,定期删除
比定时时间长一点,不会时刻的去检查过期键,删除过期键,定期删除,是每隔一段时间检查一次数据库,删除里面的过期键,很明显定期删除是定时删除和懒惰删除的一个折中。
Redis时间上采用了惰性删除和定期删除两种策略配合使用,可以更好的使CUP时间和内存区的平衡
AOF和RDB两种备份策略对过期键的处理
我们知道服务器启动的时候如果开启了RDB功能,服务器会对RDB文件进行载入
这里分两种情况
1,主服务器模式,会被文件中的键进行检查,过期的键忽略,所以过期键对主服务器不会造成影响,
2,从服务器模式,无论过期不过期全部载入数据库中,不过主服务器在进行数据同步的时候,从服务器的数据会被清空,所以一般来说,对从服务器来说也不会造成影响
然后是AOF
在执行AOF重写的时候,过期键不会被保存到重写后的AOF文件中,所以过期键不会对AOF造成影响。
主从复制的时候,主删除一个过期键,会显示的告诉从服务器
从服务器在执行客户端发送的读命令时,即使是过期键也不会删除过期键,而是像处理未过期键一样处理,从只有介绍到主的Del命令才会删除
Redis 删除失效主键的方法主要有两种:
消极方法(passive way),在主键被访问时如果发现它已经失效,那么就删除它
积极方法(active way),周期性地从设置了失效时间的主键中选择一部分失效的主键删除
1,消极方法
Redis 在实现 GET、MGET、HGET、LRANGE 等所有涉及到读取数据的命令时都会调用expireIfNeeded 函数,它存在的意义就是在读取数据之前先检查一下它有没有失效,如果失效了就删除它。其中在 expireIfNeeded 函数中调用的另外一个函数 propagateExpire,这个函数用来在正式删除失效主键之前广播这个主键已经失效的信息,这个信息会传播到两个目的地:一个是发送到 AOF文件,将删除失效主键的这一操作以 DEL Key 的标准命令格式记录下来;另一个就是发送到当前 Redis 服务器的所有 Slave,同样将删除失效主键的这一操作以 DEL Key 的标准命令格式告知这些 Slave 删除各自的失效主键。从中我们可以知道,所有作为 Slave 来运行的 Redis 服务器并不需要通过消极方法来删除失效主键。
2,积极方法
如果某些失效的主键很久等不到再次访问的话,Redis 就永远不会知道这些主键已经失效,也就永远也不会删除它们了,这无疑会导致内存空间的浪费。因此,Redis 可以使用积极的删除方法,该方法利用 Redis 的时间事件来实现,即每隔一段时间就中断一下完成一些指定操作,其中就包括检查并删除失效主键。这里我们说的时间事件的回调函数就是 serverCron,它在 Redis 服务器启动时创建,每秒的执行次数由宏定义 REDIS_DEFAULT_HZ 来指定,默认每秒钟执行10次。实际上,serverCron 这个回调函数不仅要进行失效主键的检查与删除,还要进行统计信息的更新、客户端连接超时的控制、BGSAVE 和 AOF 的触发等等,这里我们仅关注删除失效主键的实现,也就是函数 activeExpireCycle,activeExpireCycle方法实现原理就是遍历处理 Redis 服务器中每个数据库的 expires 字典表中,从中尝试着随机抽样 REDIS_EXPIRELOOKUPS_PER_CRON(默认值为10)个设置了失效时间的主键,检查它们是否已经失效并删除掉失效的主键,如果失效的主键个数占本次抽样个数的比例超过25%,Redis 会认为当前数据库中的失效主键依然很多,所以它会继续进行下一轮的随机抽样和删除,直到刚才的比例低于25%才停止对当前数据库的处理,转向下一个数据库。这里我们需要注意的是,activeExpireCycle 函数不会试图一次性处理Redis中的所有数据库,而是最多只处理 REDIS_DBCRON_DBS_PER_CALL(默认值为16),此外 activeExpireCycle 函数还有处理时间上的限制,不是想执行多久就执行多久,凡此种种都只有一个目的,那就是避免失效主键删除占用过多的CPU资源。