redis的过期策略以及内存淘汰机制

注:本文主要参考自《Redis设计与实现》
https://www.cnblogs.com/xuliangxing/p/7151812.html
https://www.cnblogs.com/sunsing123/p/11093038.html
http://www.imooc.com/article/257065?block_id=tuijian_wz

1、设置过期时间

所谓过期,就是我们插入数据的时候设置了过期时间,一班情况下有下面两种设置g

  • expire key time(以秒为单位)--这是最常用的方式
  • setex(String key, int seconds, String value)--字符串独有的方式

具体的使用方式:查看"java企业项目开发实践"的第九章 企业项目开发--分布式缓存Redis(1)和第十章 企业项目开发--分布式缓存Redis(2)

注意

  • 除了字符串自己独有设置过期时间的方法外,其他方法都需要依靠expire方法来设置时间
  • 如果没有设置时间,那缓存就是永不过期
  • 如果设置了过期时间,之后又想让缓存永不过期,使用persist key

2、两种常用的过期策略

对于上面咱们设置了过期时间的数据,那数据到期了咱们怎么删除呢?

  • 惰性删除

    • 含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
    • 优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)
    • 缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
  • 定期删除

    • 含义:每隔一段时间执行一次删除过期key操作
    • 优点:
      通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用--处理"定时删
      定期删除过期key--处理"惰性删除"的缺点
    • 缺点
      在CPU时间友好方面,不如"惰性删除",因为会导致每隔一段时间删除大量key,占用cpu
    • 难点
      合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)(这个要根据服务器运行情况来定了)

注意

  • 对于主从redis结构的从结点slave的过期key处理
    slave的key不会过期,只会等待master的key过期。如果master的key过期,或者通过LRU淘汰了key,那么会发送一条模拟的del命令给slave
  • 上边所说的数据库指的是内存数据库,默认情况下每一台redis服务器有16个数据库(关于数据库的设置,看下边代码),默认使用0号数据库,所有的操作都是对0号数据库的操作,关于redis数据库的存储结构,查看 第八章 Redis数据库结构与读写原理

  • memcached只是用了惰性删除,而redis同时使用了惰性删除与定期删除,这也是二者的一个不同点(可以看做是redis优于memcached的一点)

  • 对于惰性删除而言,并不是只有获取key的时候才会检查key是否过期,在某些设置key的方法上也会检查(eg.setnx key2 value2:该方法类似于memcached的add方法,如果设置的key2已经存在,那么该方法返回false,什么都不做;如果设置的key2不存在,那么该方法设置缓存key2-value2。假设调用此方法的时候,发现redis中已经存在了key2,但是该key2已经过期了,如果此时不执行删除操作的话,setnx方法将会直接返回false,也就是说此时并没有重新设置key2-value2成功,所以对于一定要在setnx执行之前,对key2进行过期检查

3、Redis最终采用的过期策略

惰性删除+定期删除
  • 惰性删除流程
    在进行get或setnx等操作时,先检查key是否过期,
    若过期,删除key,然后执行相应操作;
    若没过期,直接执行相应操作
  • 定期删除流程(简单而言,对指定个数个库的每一个库随机删除小于等于指定个数个过期key)
    遍历每个数据库(就是redis.conf中配置的"database"数量,默认为16)
    默认Reds每秒1次做的事情:
    1.测试随机的20个keys进行相关过期检测
    2.删除所有已经过期的keys。
    3.如果有多于25%的keys过期,重复步奏1
    这是一个平凡的概率算法,基本上的假设是,我们的样本是这个密钥控件,并且我们不断重复过期检测,直到过期的keys的百分百低于25%,这意味着,在任何给定的时刻,最多会清除1/4的过期keys

如果当前库中没有一个key设置了过期时间,直接执行下一个库的遍历
随机获取一个设置了过期时间的key,检查该key是否过期,如果过期,删除key
判断定期删除操作是否已经达到指定时长,若已经达到,直接退出定期删除。

注意:

对于定期删除,在程序中有一个全局变量current_db来记录下一个将要遍历的库,假设有16个库,我们这一次定期删除遍历了10个,那此时的current_db就是11,下一次定期删除就从第11个库开始遍历,假设current_db等于15了,那么之后遍历就再从0号库开始(此时current_db==0)

4、过期策略对RDB和AOF的影响

4.1 RDB对过期key的处理

  • 过期key对RDB没有任何影响
  • 从内存数据库持久化数据到RDB文件
    持久化key之前,会检查是否过期,过期的key不进入RDB文件
  • 从RDB文件恢复数据到内存数据库
    数据载入数据库之前,会对key先进行过期检查,如果过期,不导入数据库(主库情况)

4.2 AOF对过期key的处理

  • 从内存数据库持久化数据到AOF文件:
    • 当key过期后,还没有被删除,此时进行执行持久化操作(该key是不会进入aof文件的,因为没有发生修改命令)
    • 当key过期后,在发生删除操作时,程序会向aof文件追加一条del命令(在将来的以aof文件恢复数据的时候该过期的键就会被删掉)
  • AOF重写
    重写时,会先判断key是否过期,已过期的key不会重写到aof文件

5. redis内存淘汰机制

内存淘汰机制
Redis 的内存占用会越来越高。Redis 为了限制最大使用内存,提供了 redis.conf 中的配置参数 maxmemory。

当内存超出 maxmemory,Redis 提供了几种内存淘汰机制让用户选择,配置 maxmemory-policy:

  • noeviction:当内存超出 maxmemory,写入请求会报错,但是删除和读请求可以继续。(有些把缓存当数据库用的会用这个策略)
  • allkeys-lru:当内存超出 maxmemory,所有的 key 中,移除最少使用的key。只把 Redis 既当缓存是使用这种策略。(推荐)。
  • allkeys-random:当内存超出 maxmemory,所有的 key中,随机移除某个 key。(应该没人用吧)
  • volatile-lru:当内存超出 maxmemory,设置了过期时间 key的字典中,移除最少使用的 key。把 Redis 既当缓存,又做持久化的时候使用这种策略。
  • volatile-random:当内存超出 maxmemory,在设置了过期时间 key的字典中,随机移除某个key。
  • volatile-ttl:当内存超出 maxmemory,在设置了过期时间 key 的字典中,优先移除 ttl 小的,也就是移除马上就要过期的key。

redis 中的默认的过期策略是volatile-lru 。设置方式
可以通过命令直接设置 config set maxmemory-policy xxxx(eg:volatile-lru)

6.LRU 算法

实现 LRU 算法除了需要 key/value 字典外,还需要附加一个链表,链表中的元素按照一定的顺序进行排列。当空间满的时候,会踢掉链表尾部的元素。当字典的某个元素被访问时,它在链表中的位置会被移动到表头。所以链表的元素排列顺序就是元素最近被访问的时间顺序。

这里基于linkhashmap实现一个lru算法,可设置最大容量,淘汰最老的数据

class LRUCache extends LinkedHashMap {
    private final int CACHE_SIZE;

    public LRUCache(int size) {
        //true代表顺序存放,最新的放头部,最老的放尾部
        super((int) Math.ceil(size / 0.75) + 1, 0.75f, true);
        CACHE_SIZE = size;
    }

    /**
     * 定义移除最老的数据的规则
     *
     * @author zyh
     * @date 2021/8/4
     **/
    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > CACHE_SIZE;
    }


}
```t

你可能感兴趣的:(redis的过期策略以及内存淘汰机制)