redis主从同步异常

Posted Elaine

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis主从同步异常相关的知识,希望对你有一定的参考价值。

前几天遇到了一个主从同步异常的问题,主库是3.0版本,从库3.0只读,主库key已经过期了,但是从库还能查到。

对于过期key,只读从库不会主动删除,当key过期后,如果主库对过期key处理的不及时,那么从库还是会读到这个key。

小于3.2版本的redis会存在这个问题,大于等于3.2版本的redis有优化,在从库读取key时,会判断key是否过期,如果已经过期,

会返回nil,但是从库荣然不会主动delkey,还是依赖主库的清除过期key时,同步到从库的del操作。

/***
    redis-3.2.13
***/
int expireIfNeeded(redisDb *db, robj *key) {
    mstime_t when = getExpire(db,key);
    mstime_t now;

    if (when < 0) return 0; /* No expire for this key */

    /* Don‘t expire anything while loading. It will be done later. */
    if (server.loading) return 0;

    /* If we are in the context of a Lua script, we claim that time is
     * blocked to when the Lua script started. This way a key can expire
     * only the first time it is accessed and not in the middle of the
     * script execution, making propagation to slaves / AOF consistent.
     * See issue #1525 on Github for more information. */
    now = server.lua_caller ? server.lua_time_start : mstime();

    /* If we are running in the context of a slave, 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 now > when;

    /* Return when this key has not expired */
    if (now <= when) return 0;

    /* Delete the key */
    server.stat_expiredkeys++;
    propagateExpire(db,key);
    notifyKeyspaceEvent(NOTIFY_EXPIRED,
        "expired",key,db->id);
    return dbDelete(db,key);
}
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;
}

 

以上是关于redis主从同步异常的主要内容,如果未能解决你的问题,请参考以下文章

Redis主从同步失败案例的步步深入

百度QA金BUG集锦之《Redis主从同步失败案例的步步深入》

漏洞预警Redis主从同步代码执行漏洞

漏洞预警Redis主从同步代码执行漏洞预警通告

威胁通告Redis主从同步代码执行漏洞预警通告

搭建一个简单的redis,主从同步功能