深入剖析Redis主键失效原理及实现机制
创新互联是专业的怀柔网站建设公司,怀柔接单;提供成都网站设计、成都网站建设、外贸网站建设,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行怀柔网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!
Redis作为一款高性能的键值对存储系统,被广泛应用于缓存、消息队列、分布式锁等场景,在使用Redis的过程中,我们经常会遇到键值对失效的情况,那么Redis中主键失效的原理是什么?又是如何实现的呢?本文将深入剖析Redis主键失效的原理及实现机制。
在Redis中,主键失效主要分为两种情况:自然失效和手动失效。
1、自然失效
自然失效是指键值对在Redis中存储的时间超过了设定的过期时间,Redis会自动删除这些键值对,自然失效的实现依赖于Redis的过期键清理策略。
Redis的过期键清理策略主要有以下几种:
(1)惰性删除:在客户端访问键时,检查键是否已经过期,如果已经过期,则删除该键,并返回空,这种策略的优点是操作简单,缺点是内存使用效率不高,可能会出现大量过期键占用内存的情况。
(2)定时删除:Redis内部维护一个定时任务,按照一定的频率扫描数据库中的键,删除过期的键,这种策略可以有效地清理过期键,但会增加CPU的负担。
(3)定期删除:定期删除是定时删除的优化版本,它将定时扫描调整为周期性扫描,每次扫描只处理部分键,从而降低CPU的负担。
2、手动失效
手动失效是指通过DEL命令或其他相关命令手动删除键值对,这种情况下,键值对会立即失效。
下面我们将从源码角度分析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。内容未经允许不得转载,或转载时需注明来源: 贝锐智能