• Redis原理篇——内存回收


    Redis是基于内存存储的,性能强。Redis的性能瓶颈也在于内存,但是单节点内存不宜过大,会影响持久化或主从同步性能。

    通过配置文件来设置Redis的最大内存:
    在这里插入图片描述

    一、过期策略

    Redis是键值类型的数据库,所有的key和value保存在 Dict 结构中,在Redis数据库结构体中,有两个Dict:一个用来记录key-value,另一个记录key-TTL

    typedef struct redisDb {
        dict *dict;                 /* 存放所有key和value */
        dict *expires;              /* 存放key和对应的TTL存活时间 只包含设置了TTL的key */
        dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
        dict *ready_keys;           /* Blocked keys that received a PUSH */
        dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS (事务) */
        int id;                     /* Database ID 使用的是几号库 0~15 */
        long long avg_ttl;          /* Average TTL, 平均TTL时间 */
        unsigned long expires_cursor; /* expire检查时在dict中抽取的索引位置 */
        list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
    } redisDb;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    因此只需要查Redis中的key对应的TTL,就可以判断这个key是否过期

    删除策略

    TTL到期就立刻删除吗?

    可以设置定时器,当一个key到期了就立刻删除,但是对于大量key来说,设置大量定时器对CPU的占用率高,影响Redis主线程的性能,因此Redis不会立刻删除到期的key

    1.惰性删除:

    在访问一个key的时候检查该key的存活时间,如果已经过期了才执行删除。

    robj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags) {
         // 检查key是否过期
        expireIfNeeded(db,key);
        return lookupKey(db,key,flags);
    }
    
    robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
        robj *val;
          // 检查key是否过期
        if (expireIfNeeded(db,key) == 1) {
        /*
            ......... 略
        */ 
        
        }
       /*
            ......... 略
        */
    }
    
    int expireIfNeeded(redisDb *db, robj *key) {
          // 判断是否过期,如果没过期就返回0
        if (!keyIsExpired(db,key)) return 0;
    
        /*
            ......... 略
        */
      
    
        /* 删除过期的key */
        deleteExpiredKeyAndPropagate(db,key);
        return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    惰性删除存在的问题:

    惰性删除是在访问一个key的时候检查是否过期,如果一个key过期了但是客户端长时间没有操作,那么这个key就一直不能被删除,如果这样的key存在大量的,那么就会一直占据着内存,影响性能。

    2.周期删除

    周期性的抽样一部分过期的key,然后执行删除。
    执行周期有两种:

    • SLOW模式: Redis会设置一个定时任务serverCron(),按照server.hz的频率来执行过期key清理
    1. 执行频率受server.hz影响,默认为10,即每秒执行10次,每个执行周期100ms
    2. 执行清理耗时不超过一次执行周期的25%
    3. 逐个遍历db,抽取20个key判断是否过期
    4. 如果没达到时间上限(25ms)并且过期key比例大于10%,再进行一次抽样,否则结束
    • FAST模式: Redis的每个事件循环前会调用beforeSleep()函数,执行过期key清理
    1. 执行频率受beforeSleep()调用频率影响,但两次FAST模式间隔不低于2ms
    2. 执行清理耗时不超过1ms
    3. 依次遍历db,抽取20个key判断是否过期
    4. 如果没达到时间上限(1ms)并且过期key比例大于10%,再进行一次抽样,否则结束

    二、内存淘汰策略

    内存淘汰: Redis内存使用达到设置的阈值时,Redis主动挑选部分key删除以释放内存。

    Redis支持8种不同的策略:

    • noeviction: 不淘汰任何key,内存满时不允许写入新数据(默认策略)
    • volatile-ttl:对设置TTL的key,比较剩余的TTL值,先淘汰数值小的
    • allkeys-random: 对全体key,随机进行淘汰。
    • volatile-random:对设置了TTL的key,随机淘汰。
    • allkeys-lru:对全体key,基于LRU算法进行淘汰
    • volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰
    • allkeys-lfu:对全体key,基于LFU算法进行淘汰
    • volatile-lfu:对设置了TTL的key,基于LFU算法进行淘汰
    typedef struct redisObject {
        unsigned type:4; // 对象类型,分别为五种基本数据类型  占4个bit位
        unsigned encoding:4; // 编码方式 11 中 占4个bit位
        unsigned lru:LRU_BITS; /*LRU以秒为单位记录最近一次访问时间,长度为24bit  LFU:高16位以分钟为单位记录最近一次访问时间,低8位记录逻辑访问次数*/
        int refcount;  // 引用计数器 0表示对象无人引用 可以被回收
        void *ptr;  // 指针,指向存放实际数据的空间
    } robj;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    LFU的访问次数之所以叫逻辑访问次数,是因为并不是每次key被访问都计数。而是通过运算:

    1. 生成0~1之间的随机数R
    2. 计算1/(旧次数*lfu_log_factor+1),记录为P,lfu_log_factor默认为10
    3. 如果R,计数器+1,最大不超过255
    4. 访问次数会随时间衰减,距离上一次访问时间每隔lfu_decay_time分钟(默认1),计数器-1
  • 相关阅读:
    高月薪&高年终如何抉择?
    Java基础学习——方法的定义与调用
    docker.6-Dockerfile精讲及新型容器镜像构建技术
    JVM内存和垃圾回收-14.垃圾回收相关概念
    Redis 常见数据类型(对象类型)和应用案列
    Oracle高速批量速插入数据解决方案
    BI零售数据分析方案,看了就想拥有
    QT 实现mysql的长连接
    dockerhub注册账号失败
    新生儿吐奶的原因以及处理方法,非常实用(建议收藏)
  • 原文地址:https://blog.csdn.net/qq_52595134/article/details/128169801