今天这边主要说说redis内存的过期删除策略与内存淘汰机制。

1,删除策略

对于redis中设置了过期时间的key,我们知道对于时间到期了,这个key就会被 “删除”,但是这个key真的是一到期就会马上被删除吗?

  1. 定时删除
    就是对于没个key设置一个定时器,这种方式比较简单,效果也比较好,一旦定时器计时结束,直接删除这个key。但是唯一致命缺点就是需要维护大量定时器,cpu消耗会非常高,费性能。
  2. 惰性删除
    这个也比较好理解,就是在我们get(key)的时候,先去判断下这个key的过期时间,如果过期了,直接删掉然后返回null。但是这个也会产生问题,比如我的key一直没人用,那么就一直不会被删除,那么就可能会占用大量的redis的工作内存。
  3. 定期删除

3.1 redis在启动的时候读取配置文件hz的值,默认为10

redis的key过期时间删除 redis如何删除过期key_删除策略

3.2 每秒执行hz次serverCron()–>databasesCron()—>actveEXpireCyle()这三个方法,嵌套执行:

serverCron():扫描redis服务的api
databasesCron():循环扫描redis服务下每个db的api,一个redis默认情况下为16个db
actveEXpireCyle():循环扫描每个db下面的有过期时间的key然后做相关清除操作

3.3 actveEXpireCyle()对每个expires[*]进行逐一检测,每次执行250ms/hz

250为每秒执行serverCron()的总时间,如果你用默认配置,那么每次执行就是只有25ms,如果到了25ms执行未结束也强制return,等待下一次扫描。

3.4 对某个expires[*]检测时,随机挑选N个key检查

如果key超时,删除key
如果一轮中删除的key的数量>N*25%,说明这个db内的过期的key比较多,需要再次循环该过程执行删除操作,依次类推
如果一轮中删除的key的数量小于等于N25%,检查下一个expires[ ]
current_db用于记录actveEXpireCyle()进入哪个expires[ * ]
执行,如果时间到了,那么下次根据current_db继续执行

redis采用的是惰性删除和定期删除结合使用。

2, 淘汰机制

现在来看一个问题,如果redis的内存满了怎么办,首先最容易想到的就是数据不让进了,直接报错?

下面来看这几个配置:

# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝# 试清除已到期或即将到期的Key,
# 当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。
# Redis新的vm机制,会把Key存放内存,Value会存放在swap区
# 占用物理内存的比例,默认值为0,,表示不限制。生产环境一般根据需求设置,通常50%以上
# maxmemory <bytes>

# The default is:
#当内存使用达到最大值时,redis使用的清除策略。有以下几种可以选择(明明有6种,官方配置文件里却说有5种可以选择?):
# 1)volatile-lru   利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used ) 
# 2)allkeys-lru   利用LRU算法移除任何key 
# 3)volatile-lfu  利用lfu算法移除设置过期时间的key(lfu:最先被使用的,就是最久没被使用过的)
# 4)allkeys-lfu  利用lfu算法移除任何key 
# 5)volatile-random 移除设置过过期时间的随机key 
# 6)allkeys-random  移除随机key 
# 7)volatile-ttl   移除即将过期的key(minor TTL) 
# 8)noeviction  不移除任何key,只是返回一个写错误 。默认选项
# maxmemory-policy noeviction

# true LRU but costs more CPU. 3 is faster but not very accurate.
# LRU 和 minimal TTL 算法都不是精准的算法,但是相对精确的算法(为了节省内存),随意你可以选择样本大小进行检测。redis默认选择5个样本进行检测,你可以通过maxmemory-samples进行设置样本数。
# maxmemory-samples 5

关于淘汰策略,这边可以大致分为两类+1:
+1:noeviction 不使用任何策略,其实对于淘汰策略怎么用都不好,比如你一个正在使用的热点key,被淘汰机制误删除了,很明显对于系统又很大的影响,而且如果对于没有持久化的key,或者对于分布式锁中的lock key,删除的话都会导致一连串系统问题。所以有不使用删除策略,直接报错是最好的选择。当然前提是有相应的监控机制以及足够的物理内存。
第一种:volatile开头的,针对的淘汰目标是所有设置了过期时间的key
第二种:allkeys开头的是针对所有的key

redis的key过期时间删除 redis如何删除过期key_redis的key过期时间删除_02


关于lru和lfu算法,可见上图:

在指定时间内,比如我5秒,有三个key (key1,key2,key3),每次调用都会记录一个调用时间,以及给他的调用次数+1

如果采用lru淘汰算法,则淘汰调用次数最少的,如上图的话,可以见key2在5秒内被调用了一次,其他两种都使用了两次,那么如果采用此算法,key2会被淘汰

如果采用lfu算法,则淘汰最长时间没被使用的,可以见上面最久之前没被使用的,是key2,4s前被使用,如果采用此算法,则key2会被淘汰。

可见:
lru算法更关注调用频次,认为key使用次数越多,则这个key为活跃key,则不建议淘汰。
lfu算法更关注调用时间,认为key越长时间没被使用,认为这个key是不活跃的key,建议优先淘汰。

关于redis缓存的过期删除及内存淘汰机制基本上就上述这些!