Redis中主键失效的原理及实现机制剖析

深入剖析Redis主键失效原理及实现机制

创新互联是专业的怀柔网站建设公司,怀柔接单;提供成都网站设计、成都网站建设、外贸网站建设,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行怀柔网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!

Redis作为一款高性能的键值对存储系统,被广泛应用于缓存、消息队列、分布式锁等场景,在使用Redis的过程中,我们经常会遇到键值对失效的情况,那么Redis中主键失效的原理是什么?又是如何实现的呢?本文将深入剖析Redis主键失效的原理及实现机制。

Redis主键失效原理

在Redis中,主键失效主要分为两种情况:自然失效和手动失效。

1、自然失效

自然失效是指键值对在Redis中存储的时间超过了设定的过期时间,Redis会自动删除这些键值对,自然失效的实现依赖于Redis的过期键清理策略。

Redis的过期键清理策略主要有以下几种:

(1)惰性删除:在客户端访问键时,检查键是否已经过期,如果已经过期,则删除该键,并返回空,这种策略的优点是操作简单,缺点是内存使用效率不高,可能会出现大量过期键占用内存的情况。

(2)定时删除:Redis内部维护一个定时任务,按照一定的频率扫描数据库中的键,删除过期的键,这种策略可以有效地清理过期键,但会增加CPU的负担。

(3)定期删除:定期删除是定时删除的优化版本,它将定时扫描调整为周期性扫描,每次扫描只处理部分键,从而降低CPU的负担。

2、手动失效

手动失效是指通过DEL命令或其他相关命令手动删除键值对,这种情况下,键值对会立即失效。

Redis主键失效实现机制

下面我们将从源码角度分析Redis主键失效的实现机制。

1、自然失效实现机制

(1)惰性删除实现

在Redis中,惰性删除主要在db.c文件中的lookupKey函数中实现:

robj *lookupKey(redisDb *db, robj *key, int flags) {
    dictEntry *de = dictFind(db->dict, key->ptr);
    if (de) {
        robj *val = dictGetVal(de);
        if (expireIfNeeded(db, key) == 0) {
            /* If we are in the context of a Lua script, we return the
             * value without checking if we need to propagate the expired
             * key to AOF / slaves. */
            if (server.lua_caller) return val;
            if (flags & LOOKUP_NOTOUCH) {
                /* This is a read-only lookup, don't touch the key */
            } else {
                /* Update the access time for the ageing algorithm.
                 * Don't do it if we have a saving child, as this will trigger
                 * a copy on write madness. */
                if (!hasActiveChildProcess())
                    updateKeyAccessTime(key);
            }
            return val;
        } else {
            /* Key expired. If we are in the context of a script, it is up to
             * the script to handle the key expiration. Otherwise, we return
             * NULL to the caller, who should handle the key expiration
             * properly. */
            if (server.lua_caller) return NULL;
        }
    } else {
        /* No key */
    }
    return NULL;
}

在这个函数中,如果找到了键,会调用expireIfNeeded函数检查键是否过期,如果过期,删除键并返回0。

(2)定时删除和定期删除实现

定时删除和定期删除的实现主要在redis.c文件中的activeExpireCycle函数中:

void activeExpireCycle(int type) {
    /* This function has some global state in order to continue the work
     * incrementally across calls. */
    static unsigned int current_db = 0; /* Last DB tested. */
    static int timelimit_exit = 0;      /* Time limit hit in previous call? */
    static long long last_fast_cycle = 0; /* When last fast cycle ran. */
    int j, iteration = 0;
    int dbs_per_call = CRON_DBS_PER_CALL;
    long long start = ustime(), timelimit;
    if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
        if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION)
            return;
        last_fast_cycle = start;
    }
    /* We usually should test CRON_DBS_PER_CALL per iteration, with
     * two exceptions:
     *
     * 1) Don't test more DBs than we have.
     * 2) If last time we hit the time limit, we want to scan all DBs
     * in this iteration, as there is work to do in some DB and we don't want
     * expired keys to use memory for too much time. */
    if (dbs_per_call > server.dbnum || timelimit_exit)
        dbs_per_call = server.dbnum;
    /* We can use at max ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC percentage of CPU time
     * per iteration. Since this function gets called with a frequency of
     * server.hz times per second, the following is the max amount of time
     * we can spend in this function. */
    timelimit = 1000000 * ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC / server.hz / 100;
    /* Iterate over a few databases at a time. */
    for (j = 0; j < dbs_per_call; j++) {
        int expired;
        redisDb *db = server.db+(current_db % server.dbnum);
        /* Increment the DB now so we are sure if we run out of time
         * in the current iteration we'll restart from the next DB. */
        current_db++;
        /* Continue to expire if at the end of the cycle more than 25%
         * of the keys were expired. */
        do {
            unsigned long num, slots;
            long long now, ttl_sum;
            int ttl_samples;
            /* If there is nothing to expire try next DB. */
            if ((num = dictSize(db->dict)) == 0) {
                db->avg_ttl = 0;
                break;
            }
            /* When we have a lot of keys to expire, we get a time sample
             * to check later if the CPU time is too high. */
            if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
                start = ustime();
            /* Here we count the number of keys that are going to be
             * expired in this loop, and the number of keys we actually
             * look at to see if they should be expired. */
            expired = 0;
            ttl_sum = 0;
            ttl_samples = 0;
            if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
                slots = dictSlots(db->dict);
                /* Fast mode: Sample keys from the dictionary. */
                while (slots--) {
                    dictEntry *de = dictGetRandomKey(db->dict);
                    long long ttl;
                    if (dictGetVal(de) == NULL) {
                        /*_expired++; /* Key is already logically expired. */
                        continue;
                    }
                    ttl = dictGetSignedIntegerVal(de) - now;
                    if (activeExpireCycleTryExpire(db, de, now)) {
                        expired++;
                    } else {
                        /* If the key is non expired, add its TTL to the sum. */
                        if (ttl > 0) {
                            ttl_sum += ttl;
                            ttl_samples++;
                        }
                    }
                }
            } else {
                /* Slow mode: Check every key. */
                dictEntry *de = dictGetSafeIterator(db->dict);
                while ((de = dictNext(de)) != NULL) {
                    long long ttl;
                    if (dictGetVal(de) == NULL) {
                        expired++;
                        continue;
                    }
                    ttl = dictGetSignedIntegerVal(de) - now;
                    if (activeExpireCycleTryExpire(db, de, now)) {
                        expired++;
                    } else {
                        /* If the key is non expired, add its TTL to the sum. */
                        if (ttl > 0) {
                            ttl_sum += ttl;
                            ttl_samples++;
                        }
                    }
                }
                dictReleaseIterator(de);
            }
            /* Update the average TTL stats for this DB. */
            if (ttl_samples) {
                long long avg_ttl = ttl_sum / ttl_samples;
                /* Do a simple running average with a few samples.
                 * We just use the current estimate if the previous one
                 * was zero, otherwise we combine the two. */
                if (db->avg_ttl == 0) {
                    db->avg_ttl = avg_ttl;
                } else {
                    db->avg_ttl = (db->avg_ttl/2) + (avg_ttl/2);
                }
            }
            /* We can't block forever here even if there are many keys to
             * expire. So after a given amount of milliseconds return to the
             * caller waiting for the other active expire cycle. */
            if (ustime() - start > timelimit) {
                timelimit_exit = 1;
                break;
            }
        } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
    }
    /* Update our estimate of keys existing but yet to be expired. */
    updateKeyspaceEvents();
    /* We don't repeat the cycle if there are less than 25% of keys to
     * expire in the DB we just handled, however if we exited because of the
     * time limit, we'll try again later

网页题目:Redis中主键失效的原理及实现机制剖析
标题路径:http://www.mswzjz.cn/qtweb/news38/247738.html

攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能