redis数据清除策略
redis因为是在内存里操作数据的,默认内存大小为0,则无上限(跟随机器的内存),可以通过设置maxmemory 来配置最大的可使用内存。redis里的数据清除策略有两种,但是两种策略互不影响(同时存在),一种是仅包含过期时间的数据清除策略,一种是当内存空间不足的时候数据清除策略。
一、过期键设置
redis有四个不同的命令可以用于设置键的生存时间或过期时间。
- expire:将键的生存时间设置为ttl秒
- pexpire:将键的生存时间设为ttl毫秒
- expireat:将键的过期时间设置为timestamp所指定的秒数时间戳
- pexpireat:将键的过期时间设置为timestamp所指定的毫秒数时间戳
前面三个expire、pexpire、expireat最终都是由pexpireat来实现,上面的三个命令会经过转换为pexpireat命令
redisDb结构的expires字典保存了数据库所有键的过期时间,我们称这个字典为过期字典。
- 过期字典的键是一个指针,这个指针指向空间中的某个键对象
- 过期字典的值是一个long类型的整数,这个整数保存了键所指向的数据库键的过期时间---一个毫秒精度的unix时间戳
通过过期字典,程序可以判断一个键是否过期
- 检查指定键是否存在过期字典当中,如果存在则获取过期时间
- 检查当前的unix时间戳是否大于键的过期时间,如果是的话表示键已过期,否则键未过期
二、过期键的删除策略
当我们对指定键进行过期时间操作的时候,实际上是对应在redisDb中的expires字典进行相应的操作,比如设置一个从未设置过期时间key来指定过期时间,则是在expires中添加一个k(指向db中key的指针,即key的真实地址)-v(到期的时间戳)结构,移除键的过期时间(PERSIST)则意味着删除expires中对应的键的过期时间戳。TTL命令可以以秒位单位返回键的剩余生存时间,PTTL以毫秒返回键的过期时间,结合expires字典结构,则是计算当前时间和过期时间戳的一个差值返回到客户端。redis包含以下三种删除策略。
2.1 惰性删除
惰性删除是指当数据到期的时候,不做删除,等待下次访问的时候进行删除。
这种策略的优点在于节约了CPU的性能,但是占用了内存,因为过期的数据一直存放在内存当中。即内存换CPU。
2.2 定时删除
对key设置过期时间的时候,创建一个定时器(定时器执行时间为过期的时间),当设置key的过期,由定时器执行对key的删除操作(这里的删除是将key和expires区域的数据全部删除)
这种策略的有点是节约内存,快速的释放内存,但是会大量的占用CPU,会影响redis的响应时间和指令吞吐量,即CPU换内存。但是这种方式不推荐使用,也几乎没人使用,因为一个过期key就会有一个定时器,严重影响性能
2.3 定期删除
定期删除的步骤如下:
- 每秒钟执行10(redis启动时,会读取配置server.hz的值,默认10次)次serverCron()→ databasesCron()(对redis中每一个库进行访问)→activeExpireCycle()(对每一个expires[*]逐一进行检测,每次执行250ms/server.hz)
- 对某一个expires[*]检测时,随机挑选W(ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值,redis默认是20)个key检测
- 如果key超时,删除key
- 如果一轮中删除的key数量>W25%,继续检测该expires(循环该过程)
- 如果一轮中删除的key数量<=W*25%,则检查下一个expires
- 参数current_db用于记录activeExpireCycle()进入哪个expires[*]执行(防止轮询时把某个expires落下)
- 如果activeExpireCycle()执行时间到期,下次从current_db继续向下执行
简单说就是周期性轮询redis库中的有时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度
key设置CPU的峰值,检测的频率也可以自定义设置;内存的压力不大,长期占用内存的数据会被持续清理
2.4 redis的默认删除策略
redis实际是使用惰性删除+定期删除两种策略配合的,通过这两种策略可以很好的利用CPU的时间和避免了内存的占用。
如果单独使用定时删除会出现一个问题,如果过期的key很多的时候当keyA已经过期了,但是它没有被检测时,如果服务器要去获取这个key,就会出现问题,因为keyA其实已经过期,但是还存在内存当中,并且能够成功取到。redis通过两种策略配合规避掉这一种风险。
redis对于惰性删除的策略实现基于expireIfNeeded函数实现,所有的读写数据库命令在执行之前都会对调用该函数。对于定期删除策略是通过activeExpireCycle函数去执行,serverCron函数执行时,activeExpireCycle函数就会被调用,规定的时间里面分多次遍历服务器的expires字典随机检查一部分key的过期时间,并删除其中的过期key。
三、AOP、RDB和复制功能对过期键处理
3.1 生成RDB文件
在执行save或bgsave创建rdb文件时,程序会对数据库中的键进行检查,已过期的键不会保存到rdb文件当中
3.2 载入RDB文件
在加载rdb文件的时候:
- 如果服务器以主服务器模式运行,那么载入rdb文件时,程序会对文件中保存的键进行检查,未过期的键会被载入到数据库当中,而过期键则会被忽略,所以过期键对载入RDB的主服务器不会造成影响
- 如果服务器以从服务器模式运行,那么在载入rdb文件时,文件中保存的所有key不论是否过期,都会被载入到数据库当中。但是由于主从服务器进行数据同步的时候,从服务器的数据库会被清空,所以一般来讲,过期键对载入rdb的从服务器也不会造成影响。这里可以理解为在载入从服务器的时候,需要同步主服务器的数据,只是短暂的不一致情况
3.3 AOF文件写入
aof写入包含两个类型,正常写入和重写:
- 正常写入:如果数据库中的某个键已经过期,但是还没有被惰性删除和定期删除。那么AOF文件不会因为这个过期键而产生任何影响。当过期键被惰性删除或定期删除的时候,程序会向AOP追加一个del命令,来显示的记录该键已经被删除
- 重写aof文件:当aof文件大小达到设置的最大限制时,会进行重写,这里和生成rdb文件的结果类型,在aof重写的过程当中会对数据库的键进行过滤,已经过期的键不会被保存到重写的aof文件当中
3.4 复制
服务器运行在复制模式(具有主备)下,从服务器的过期键删除动作由服务器控制。
- 主服务器删除一个过期键以后,会显示的向所有的从服务器发送一个del命令,告诉从服务器删除这个键
- 从服务器在执行客户端发送的读命令时,即使碰到过期键也不会将过期键删除,而是继续像处理未过期的键一样来处理过期键
- 从服务器在接收到主服务器发送的del命令之后才会删除过期键
这里会有一个问题,主服务器如果没有发送删除过期键命令到从服务器,那从服务器的键就当成是正常使用,即使已经过期了
四、内存淘汰策略
Redis配置文件中可以设置maxmemory,内存的最大使用量,到达限度时会执行内存淘汰机制,默认是no-eviction
淘汰策略 | 描述 |
---|---|
volatile-lru | 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰 |
volatile-lfu | 从已设置过期时间的数据集中挑选最不经常使用的数据淘汰 |
volatile-ttl | 从已设置过期时间的数据集中挑选将要过期的数据淘汰 |
volatile-random | 从已设置过期时间的数据集中挑选任意数据淘汰 |
allkeys-lru | 当内存不足写入新数据时淘汰最近最少使用的Key |
allkeys-random | 当内存不足写入新数据时随机选择key淘汰 |
allkeys-lfu | 当内存不足写入新数据时移除最不经常使用的Key |
no-eviction | 当内存不足写入新数据时,写入操作会报错,同时不删除数据 |
它们的触发条件都是Redis使用的内存达到阈值时。
- volatile为前缀的策略都是从已过期的数据集中进行淘汰。
- allkeys为前缀的策略都是面向所有key进行淘汰。
- LRU(least recently used)最近最少用到的。淘汰最后被访问时间最久的元素
- LFU(Least Frequently Used)最不常用的。淘汰最近访问频率最小的元素。