• Redis缓存Key过期原理和内存淘汰策略


    Key过期原理

    Redis通过字典(Redis字典通过哈希表来实现)来存储键的过期时间,字典的键是指向RedisDb的指针(使用指针可以避免浪费),字典的值是一个毫秒的时间戳,所以当前时间大于字典值的时候这个键就过期了,就可以对这个键进行删除(删除一个键不仅要删除redisDb数据库中的键,也要删除过期字典中的键)。

    通过pexpireat命令来设置过期时间的命令,其他命令最终也会转换成pexpireat。给一个键设置过期时间,就是将这个键的指针及给定的到期时间戳加到过期字典中。

    在这里插入图片描述

    Redis删除过期Key的策略

    • 被动式删除

      当客户端尝试访问某个key的时候,发现当前key已经过期了,就直接删除这个key。

      如果一些过期的key没有被访问,就会导致这些key一直存在并且占用内存。

    • 主动式删除

      Redis会定期扫描过期的key并且进行删除,并且Redis底层会通过限制删除操作执行的时长和频率来减少删除操作对CPU影响。其原理如下:

      • 从过期的key中随机选取20个key,删除其中已经过期的key;
      • 在这20个key中如果超过了25%的过期key,则重新执行当前步骤。实际上这是利用了一种概率算法。

    被动式删除对CPU更加友好,主动式删除对内存更加友好。所以Redis采用的是:被动式删除+主动式删除

    Redis内存淘汰策略

    背景

    仅仅通过上面的删除过期Key策略在业务实际场景中可能会漏掉了很多过期的key,如果没有被及时删除掉可能会引发内存溢出

    Redis内存淘汰策略

    1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
    2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
    3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
    4. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
    5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
    6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
    7. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
    8. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key

    相关源码

    源码路径:redis/src/server.h

    typedef struct redisDb {
        dict *dict;                 //数据库键空间,保存着数据库中所有键值对
        dict *expires;              // 过期字典,保存着键的过期时间
        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 */
        long long avg_ttl;          /* Average TTL, just for stats */
        unsigned long expires_cursor; /* Cursor of the active expire cycle. */
        list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
        clusterSlotToKeyMapping *slots_to_keys; /* Array of slots to keys. Only used in cluster mode (db 0). */
    } redisDb;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    源码路径:redis/src/dict.h

    typedef struct dict {
        dictType *type; //dictType中定义了很多dict中常用的方法,具体见dictType
        void *privdata; //一些私有数据
        dictht ht[2]; //两个dictht数组,正常只有一个在用,只有当rehash时才会使用两个
        long rehashidx; /* dic是否正在进行rehash,当不为-1时,说明正在rehash */
        unsigned long iterators; /* 遍历器的个数*/
    } dict;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    typedef struct dictht {
        dictEntry **table; //dictEntry数组保存真正的键值对
        unsigned long size; //table数组的长度
        unsigned long sizemask; //table数组的长度-1,为了方便计算键值对保存在table中的位置而定义的变量
        unsigned long used; //dictht中已经存在的键值对数量
    } dictht;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    typedef struct dictEntry {
        void *key;  //键值对中的key
        union {
            void *val;
            uint64_t u64;
            int64_t s64;
            double d;
        } v; //键值对中的value,对不同的value选取不同的值
        struct dictEntry *next; //指向下一个dictEntry的指针
    } dictEntry;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    SpringBoot集成websocket及上线前服务器nginx配置websocket
    SQL语法之DML、DQL和DCL语句
    SpringBoot学习笔记(六)——Redis数据库
    需求变化频繁的情况下,如何实施自动化测试
    IJ中PHP环境的搭建和使用教程
    持续进化,快速转录,Faster-Whisper对视频进行双语字幕转录实践(Python3.10)
    每日4道算法题——第002天
    网页去色变黑白+网页黑白恢复为彩色
    Samb共享用户的设置和修改Linux用户的id号,修改Linux组的id号,加入组,删除组成员等
    读书-人生算法
  • 原文地址:https://blog.csdn.net/u011077966/article/details/126880828