• Redis系列18:过期数据的删除策略


    Redis系列1:深刻理解高性能Redis的本质
    Redis系列2:数据持久化提高可用性
    Redis系列3:高可用之主从架构
    Redis系列4:高可用之Sentinel(哨兵模式)
    Redis系列5:深入分析Cluster 集群模式
    追求性能极致:Redis6.0的多线程模型
    追求性能极致:客户端缓存带来的革命
    Redis系列8:Bitmap实现亿万级数据计算
    Redis系列9:Geo 类型赋能亿级地图位置计算
    Redis系列10:HyperLogLog实现海量数据基数统计
    Redis系列11:内存淘汰策略
    Redis系列12:Redis 的事务机制
    Redis系列13:分布式锁实现
    Redis系列14:使用List实现消息队列
    Redis系列15:使用Stream实现消息队列
    Redis系列16:聊聊布隆过滤器(原理篇)
    Redis系列17:聊聊布隆过滤器(实践篇)

    1 介绍

    通过前面的章节,我们知道,Redis 是一个kv型数据库,我们所有的数据都是存放在内存中的,但是内存是有大小限制的,不可能无限制的增量。
    想要把不需要的数据清理掉,一种办法是直接删除,这个咱们前面章节有详细说过;另外一种就是设置过期时间,缓存过期后,由Redis系统自行删除。
    这边需要注意的是,缓存过期之后,并不是马上删除的,那Redis是怎么删除过期数据的呢?主要通过两个方式

    • 惰性删除
    • 通过定时任务,定期选取部分数据删除

    2 Redis缓存过期命令

    我们通过以下指令给指定key的缓存设置过期时间,如果都没设置过期时间, key 将一直存在,直到我们使用 Del 的命令明确删除掉。

    # 缓存时间过期命令,参考如下
    EXPIRE key seconds [ NX | XX | GT | LT] 
    

    Redis 7.0 开始,EXPIRE 添加了 NX、XX和GT、LT 选项,分别代表如下:

    • NX:仅当Key没有过期时设置过期时间
    • XX:仅当Key已过期时设置过期时间
    • GT:仅当新到期时间大于当前到期时间时设置到期时间
    • LT:仅当新到期时间小于当前到期时间时设置到期时间

    其中,GT、LT和NX选项是互斥的,下面是官方的测试用例:

    redis> SET mykey "Hello"
    "OK"
    redis> EXPIRE mykey 10
    (integer) 1
    redis> TTL mykey
    (integer) 10
    redis> SET mykey "Hello World"
    "OK"
    redis> TTL mykey
    (integer) -1
    redis> EXPIRE mykey 10 XX
    (integer) 0
    redis> TTL mykey
    (integer) -1
    redis> EXPIRE mykey 10 NX
    (integer) 1
    redis> TTL mykey
    (integer) 10
    
    

    3 两种过期数据的删除方式

    我们前面说过,Redis删除过期数据主要通过以下两个方式,我们一个个来看:

    • 惰性删除
    • 通过定时任务,定期选取部分数据删除

    3.1 惰性删除

    惰性删除比较简单,当客户端请求过来查询我们的key的时候,先对key做一下检查,如果没过期则返回缓存数据,如果过期,则删除缓存,重新从数据库中获取数据。
    这样,我们就把删除过期数据的主动权交给了访问请求的客户端,如果客户端一直没请求,那这个过期缓存可能就长时间得不到释放。

    Redis的源码 src/db.c 中的 expireIfNeeded 方法 就是实现以上惰性删除逻辑的,我们来看看:

    int expireIfNeeded(redisDb *db, robj *key, int force_delete_expired) {
        // 对于未过期的key,直接 return 0
        if (!keyIsExpired(db,key)) return 0;	
    	
    	 /* If we are running in the context of a slave, instead of
         * evicting the expired key from the database, we return ASAP:
         * the slave key expiration is controlled by the master that will
         * send us synthesized DEL operations for expired keys.
         *
         * Still we try to return the right information to the caller,
         * that is, 0 if we think the key should be still valid, 1 if
         * we think the key is expired at this time. */
        if (server.masterhost != NULL) {
            if (server.current_client == server.master) return 0;
            if (!force_delete_expired) return 1;
        }
    
      /* If clients are paused, we keep the current dataset constant,
         * but return to the client what we believe is the right state. Typically,
         * at the end of the pause we will properly expire the key OR we will
         * have failed over and the new primary will send us the expire. */
        if (checkClientPauseTimeoutAndReturnIfPaused()) return 1;
    
        /* Delete the key */
        deleteExpiredKeyAndPropagate(db,key);
        return 1;
    }
    

    3.2 定期删除

    刚才前面说过了,仅靠客户端访问来对过期缓存执行删除远远不够,因为有的 key 过期了,但客户端一直没请求,那这个过期缓存可能就长时间甚至永远得不到释放。
    所以除了惰性删除,Redis 还可以通过定时任务的方式来删除过期的数据。定时任务的发起的频率由redis.conf配置文件中的hz来进行配置

    # 代表每1s 运行 10次
    hz 10
    

    Redis 默认每 1 秒运行 10 次,也就是每 100 ms 执行一次,每次随机抽取一些设置了过期时间的 key(这边注意不是检查所有设置过期时间的key,而是随机抽取部分),检查是否过期,如果发现过期了就直接删除。
    该定时任务的具体流程如下:

    1. 定时serverCron方法去执行清理,执行频率根据redis.conf中的hz配置的值
    2. 执行清理的时候,不是去扫描所有的key,而是去扫描所有设置了过期时间的key(redisDb.expires)
    3. 如果每次去把所有过期的key都拿过来,那么假如过期的key很多,就会很慢,所以也不是一次性拿取所有的key
    4. 根据hash桶的维度去扫描key,扫到20(可配)个key为止。假如第一个桶是15个key ,没有满足20,继续扫描第二个桶,第二个桶20个key,由于是以hash桶的维度扫描的,所以第二个扫到了就会全扫,总共扫描35个key
    5. 找到扫描的key里面过期的key,并进行删除
    6. 删除完检查过期的 key 超过 25%,继续执行4、5步

    image

    其他注意点:

    • 为何不扫描所有key进行过期缓存元素删除:Redis本身就是高速缓存,如果每次检查大量的key,无论在CPU和内存的的使用率上都会特别高,Redis集群越大,风险越大。
    • 分片模式下的删除同步:无论定时删除还是惰性删除。master 会生成删除的指令记录到 AOF 和 slave 节点。

    4 总结

    无论是惰性删除还是定期删除,都可能存在删除不尽的情况:无法删除完全,比如每次删除完过期的 key 还是超过 25%,且这些 key 再也不会被客户端访问。
    如果长时间持续下去,可能会导致内存耗尽,为了避免这种糟糕情况,Redis会有一个完善的内存淘汰机制来保障。下一节我们会着重来介绍下内存淘汰机制。

  • 相关阅读:
    python将中文标点符号转换成英文标点符号然后再替换成回车符实现换行
    Python爬虫有哪些库,分别怎么用
    工商银行卡安全码怎么看
    数据库系统原理与应用教程(079)—— MySQL 练习题:操作题 181-185(二十三):综合练习
    【创新项目探索】大数据服务omnidata-hive-connector介绍
    数据库管理工具Navicat16版本,功能优化全新详解
    智慧城市总体规划与设计大纲
    Java和JavaScript区别与联系
    [algorithm] 二叉树的DFS与BFS算法 (Java) -- 痛定思痛 彻底搞懂
    程序员如何写游戏搞钱?
  • 原文地址:https://www.cnblogs.com/wzh2010/p/17205436.html