Redis学习--Redis对过期键的处理

发布时间 2023-06-06 16:23:31作者: TeyGao

Redis做RDB备份时对已过期键的处理

【待验证】执行SAVE和BGSAVE所产生的RDB文件不会包含"已过期键"。

Redis做RDB加载时对已过期键的处理

在Redis实例加载RDB时,会先加载"已过期键",如果当前实例为主节点则会通过decrRefCount操作来删除"已过期键"。

* an RDB file from disk, either at startup, or when an RDB was
* received from the master. In the latter case, the master is
* responsible for key expiry. If we would expire keys here, the
* snapshot taken by the master may not be reflected on the slave. */
if (server.masterhost == NULL && !loading_aof && expiretime != -1 && expiretime < now) {
	decrRefCount(key);
	decrRefCount(val);
	continue;
}

Redis做AOF重写时对已过期键的处理

【待验证】执行AOF重写时生成AOF文件不会包含已过期键。

Redis做AOF加载时对已过期键的处理

【未查到】猜测加载AOF时不会判断KEY是否已过期

Redis做复制初始化对已过期键的处理

【待验证】复制同步请求触发的RDB备份不包含"已过期键"

Redis做复制同步时对已过期键的处理

当Redis主节点删除Key或清理过期Key时,会产生一条DEL命令并追加到AOF日志和传递给Redis从节点,Redis从节点接收到"DEL命令"并执行删除对于Key。

应用请求Redis主实例时对已过期键的处理

在Redis主节点上每次对Key执行操作时,都是调用expireIfNeeded来判断是否已过期:

/* This function is called when we are going to perform some operation
 * in a given key, but such key may be already logically expired even if
 * it still exists in the database. The main way this function is called
 * is via lookupKey*() family of functions.
 *
 * The behavior of the function depends on the replication role of the
 * instance, because slave instances do not expire keys, they wait
 * for DELs from the master for consistency matters. However even
 * slaves will try to have a coherent return value for the function,
 * so that read commands executed in the slave side will be able to
 * behave like if the key is expired even if still present (because the
 * master has yet to propagate the DEL).
 *
 * In masters as a side effect of finding a key which is expired, such
 * key will be evicted from the database. Also this may trigger the
 * propagation of a DEL/UNLINK command in AOF / replication stream.
 *
 * The return value of the function is 0 if the key is still valid,
 * otherwise the function returns 1 if the key is expired. */
int expireIfNeeded(redisDb *db, robj *key) 

应用请求Redis从实例时对已过期键的处理

在Redis从节点上每次对Key执行操作时,即使Key已经过期也不会触发删除,且会返回已过期Key的值。

Redis从节点不会主动删除Key,除非接收到Redis主节点同步过来的"DEL命令"。

/* Lookup a key for read operations, or return NULL if the key is not found
 * in the specified DB.
 *
 * As a side effect of calling this function:
 * 1. A key gets expired if it reached it's TTL.
 * 2. The key last access time is updated.
 * 3. The global keys hits/misses stats are updated (reported in INFO).
 *
 * This API should not be used when we write to the key after obtaining
 * the object linked to the key, but only for read only operations.
 *
 * Flags change the behavior of this command:
 *
 *  LOOKUP_NONE (or zero): no special flags are passed.
 *  LOOKUP_NOTOUCH: don't alter the last access time of the key.
 *
 * Note: this function also returns NULL is the key is logically expired
 * but still existing, in case this is a slave, since this API is called only
 * for read operations. Even if the key expiry is master-driven, we can
 * correctly report a key is expired on slaves even if the master is lagging
 * expiring our key via DELs in the replication link. */
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
    robj *val;

    if (expireIfNeeded(db,key) == 1) {
        /* Key expired. If we are in the context of a master, expireIfNeeded()
         * returns 0 only when the key does not exist at all, so it's safe
         * to return NULL ASAP. */
        if (server.masterhost == NULL) return NULL;

        /* However if we are in the context of a slave, expireIfNeeded() will
         * not really try to expire the key, it only returns information
         * about the "logical" status of the key: key expiring is up to the
         * master in order to have a consistent view of master's data set.
         *
         * However, if the command caller is not the master, and as additional
         * safety measure, the command invoked is a read-only command, we can
         * safely return NULL here, and provide a more consistent behavior
         * to clients accessign expired values in a read-only fashion, that
         * will say the key as non exisitng.
         *
         * Notably this covers GETs when slaves are used to scale reads. */
        if (server.current_client &&
            server.current_client != server.master &&
            server.current_client->cmd &&
            server.current_client->cmd->flags & CMD_READONLY)
        {
            return NULL;
        }
    }
    val = lookupKey(db,key,flags);
    if (val == NULL)
        server.stat_keyspace_misses++;
    else
        server.stat_keyspace_hits++;
    return val;
}