redis缓存删除策略及重大隐患
Redis对于过期键有三种清除策略:
被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key
注:被动删除:只有key被操作时(如GET),REDIS才会被动检查该key是否过期,如果过期则删除之并且返回NIL。
主动删除:由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期主动淘汰一批已过期的key
当前已用内存超过maxmemory限定时,触发主动清理策略,策略包含以下几种:
1.volatile-lru(least recently used):最近最少使用算法,从设置了过期时间的键中选择空转时间最长的键值对清除掉;
2.volatile-lfu(least frequently used):最近最不经常使用算法,从设置了过期时间的键中选择某段时间之内使用频次最小的键值对清除掉;
3.volatile-ttl:从设置了过期时间的键中选择过期时间最早的键值对清除;
4.volatile-random:从设置了过期时间的键中,随机选择键进行清除;
5.allkeys-lru:最近最少使用算法,从所有的键中选择空转时间最长的键值对清除;
6.allkeys-lfu:最近最不经常使用算法,从所有的键中选择某段时间之内使用频次最少的键值对清除;
7.allkeys-random:所有的键中,随机选择键进行删除;
8.noeviction:不做任何的清理工作,在redis的内存超过限制之后,所有的写入操作都会返回错误;但是读操作都能正常的进行;
前缀为volatile-和allkeys-的区别在于二者选择要清除的键时的字典不同,volatile-前缀的策略代表从redisDb中的expire字典中选择键进行清除;allkeys-开头的策略代表从dict字典中选择键进行清除。
主动删除
Redis会周期性的随机测试一批设置了过期时间的key并进行处理。测试到的已过期的key将被删除。典型的方式为,Redis每秒做10次如下的步骤:
随机测试100个设置了过期时间的key
删除所有发现的已过期的key
若删除的key超过25个则重复步骤1 注:这里的意思是这一秒内会重复第一个步骤,如果没有超过25个那就不去重复步骤1,就意味着有900毫秒处于空转,要等到下一秒才会去重复步骤1。
这是一个基于概率的简单算法,基本的假设是抽出的样本能够代表整个key空间,redis持续清理过期的数据直至将要过期的key的百分比降到了25%以下。这也意味着在任何给定的时刻已经过期但仍占据着内存空间的key的量最多为每秒的写操作量除以4。
Redis-3.0.0中的默认值是10,代表每秒钟调用10次后台任务。
除了主动淘汰的频率外,Redis对每次淘汰任务执行的最大时长也有一个限定,这样保证了每次主动淘汰不会过多阻塞应用请求。
实验一:验证主动删除
数据库db254
常驻key:300 过期时间48小时
临时key: 30 过期时间10秒
查看key总量:dbsize
~/real_scripts/redis_batch_import.sh now 10 254
查看主动删除过程:
实验二:验证惰性删除暴露主动删除的缺陷
数据库db255
常驻key:3000000 过期时间48小时
临时key:300 过期时间5秒
步骤一:演示惰性删除
查看key总量
dbsize
查看常驻key的过期时长(单位秒)
ttl names1
触发惰性删除
set aa bb ex 10
10秒后
get aa
步骤二:暴露主动删除策略缺陷
插入300个临时key
~/real_scripts/redis_batch_import.sh now 100 255
查看key总量
dbsize
等待十秒再次查看key总量
dbsize
触发临时key 惰性删除
get nownamess1
查看key总量
dbsize
超过10秒通过统计发现过期的key 还没有被回收仍然占据内存,当我们通过访问这个key时,再次进行统计发现这个key已经被回收,触发了惰性删除策略。为什么过了10秒key仍然没有被回收,难道主动删除策略没有起作用吗,其实不是详见如下分析:
主动删除的缺陷如下:
每秒随机抽样100个理想情况下大概需要 3000000÷100=30000÷3600= 8.333333小时才能将总体全部抽样完成,也就是说当常驻key远远大于临时key时redis主动删除策略需要花费大量时间进行随机抽样,才能达到主动删除的目的。
这里会存在一个非常大的隐患,如果在这8小时内,继续有大key 涌入,就很可能造成内存穿透,引发key随机丢失的情况。
比如涌入70000个key(每个key 为147KB):147 × 70000÷1024÷1024 = 9.81GB
如果涌入14万个以上的key那么我们需要预留20GB 的内存空间才能保证不引发内存穿透。