淘先锋技术网

首页 1 2 3 4 5 6 7

1、什么是数据过期

redis是一个内存行的kv型数据库,我们所有的数据都是存放在内存中的,但是内存是有大小限制的。所以我们通常会对存入的数据设置过期时间,这样方便在数据过期后进行删除。
那redis是怎么删除过期数据的呢?只要是通过两个方式,一个是惰性删除,一个是通过定时任务
最大的内存由配置项配置:

# maxmemory <bytes>

1.1 惰性删除

在redis里面,我们是在每次进行访问的时候来确认下key是否过期,如果过期就删除他。
db.c中源码中有关于这块逻辑

int expireIfNeeded(redisDb *db, robj *key) {
    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) 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;
}

1.2、定时任务删除

除了惰性删除外,redis还可以通过定时任务的方式来删除过期的数据。定时任务的发起的频率由redis.conf配置文件中的hz来进行配置

hz 10

该定时任务的具体流程如下:

  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. 如果取了400个空桶,或者扫描的删除比例跟扫描的总数超过10%,继续
    执行4、5步。
  7. 也不能无限的循环,循环16次后回去检测时间,超过指定时间会跳出

2、Redis淘汰策略

redis的内存是有限的,如果当内存快满的时候但是有没有可以过期的key进行删除的时候,这个时候又会导致redis不可写入数据了。因此我你们需要一种策略来保证redis的可用性,这个就是redis的淘汰策略
redis中总共由8中淘汰策略,默认的淘汰策略是noeviction

# volatile-lru -> Evict using approximated LRU, only keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU, only keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key having an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.

这八种策略是:
1、基于设置过期时间key的lru淘汰算法
2、基于所有key的lru淘汰算法
3、基于设置过期时间key的LFU淘汰算法
4、基于所有key的LFU淘汰算法
5、基于设置了过期时间key的随机淘汰算法
6、基于所有key的随机淘汰算法
7、淘汰失效时间最短的key
8、不进行任何淘汰,默认使用策略

整体的淘汰策略:

1.首先,我们会有个淘汰池,默认大小是16,并且里面的数据是末尾淘汰
制。
2.每次指令操作的时候,自旋会判断当前内存是否满足指令所需要的内存
3.如果当前内存不能满足,会从淘汰池中的尾部拿取一个最适合淘汰的数据
3.1 会取样(配置 maxmemory-samples)从Redis中获取随机获
取到取样的数据 解决一次性读取所有的数据慢的问题
3.2 在取样的数据中,根据淘汰算法 找到最适合淘汰的数据
3.3 将最合适的那个数据跟淘汰池中的数据比较,是否比淘汰池
中的更适合淘汰,如果更适合,放入淘汰池
3.4 按照适合的程度进行排序,最适合淘汰的放入尾部
4.将需要淘汰的数据从Redis删除,并且从淘汰池移除