深入理解Redis中的主键失效及其实现机制
来源:互联网 发布:linux 重启后挂载失败 编辑:程序博客网 时间:2024/03/29 13:32
原文地址:http://blog.sina.com.cn/s/blog_48c95a190101e5hv.html
代码段一:
typedef struct redisDb { dict *dict; dict *expires; dict *blocking_keys; dict *ready_keys; dict *watched_keys; int id;} redisDb;
代码段二:
int expireIfNeeded(redisDb *db, robj *key) { 获取主键的失效时间 long long when = getExpire(db,key); 假如失效时间为负数,说明该主键未设置失效时间(失效时间默认为-1),直接返回0 if (when < 0) return 0; 假如Redis服务器正在从RDB文件中加载数据,暂时不进行失效主键的删除,直接返回0 if (server.loading) return 0; 假如当前的Redis服务器是作为Slave运行的,那么不进行失效主键的删除,因为Slave 上失效主键的删除是由Master来控制的,但是这里会将主键的失效时间与当前时间进行 一下对比,以告知调用者指定的主键是否已经失效了 if (server.masterhost != NULL) { return mstime() > when; } 如果以上条件都不满足,就将主键的失效时间与当前时间进行对比,如果发现指定的主键 还未失效就直接返回0 if (mstime() <= when) return 0; 如果发现主键确实已经失效了,那么首先更新关于失效主键的统计个数,然后将该主键失 效的信息进行广播,最后将该主键从数据库中删除 server.stat_expiredkeys++; propagateExpire(db,key); return dbDelete(db,key);}
代码段三:
void propagateExpire(redisDb *db, robj *key) { robj *argv[2]; shared.del是在Redis服务器启动之初就已经初始化好的一个常用Redis对象,即DEL命令 argv[0] = shared.del; argv[1] = key; incrRefCount(argv[0]); incrRefCount(argv[1]); 检查Redis服务器是否开启了AOF,如果开启了就为失效主键记录一条DEL日志 if (server.aof_state != REDIS_AOF_OFF) feedAppendOnlyFile(server.delCommand,db->id,argv,2); 检查Redis服务器是否拥有Slave,如果是就向所有Slave发送DEL失效主键的命令,这就是 上面expireIfNeeded函数中发现自己是Slave时无需主动删除失效主键的原因了,因为它 只需听从Master发送过来的命令就OK了 if (listLength(server.slaves)) replicationFeedSlaves(server.slaves,db->id,argv,2); decrRefCount(argv[0]); decrRefCount(argv[1]);}
代码段四:
if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) { redisPanic("create time event failed"); exit(1);}
代码段五:
void activeExpireCycle(void) { 因为每次调用activeExpireCycle函数不会一次性检查所有Redis数据库,所以需要记录下 每次函数调用处理的最后一个Redis数据库的编号,这样下次调用activeExpireCycle函数 还可以从这个数据库开始继续处理,这就是current_db被声明为static的原因,而另外一 个变量timelimit_exit是为了记录上一次调用activeExpireCycle函数的执行时间是否达 到时间限制了,所以也需要声明为static static unsigned int current_db = 0; static int timelimit_exit = 0; unsigned int j, iteration = 0; 每次调用activeExpireCycle函数处理的Redis数据库个数为REDIS_DBCRON_DBS_PER_CALL unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; long long start = ustime(), timelimit; 如果当前Redis服务器中的数据库个数小于REDIS_DBCRON_DBS_PER_CALL,则处理全部数据库, 如果上一次调用activeExpireCycle函数的执行时间达到了时间限制,说明失效主键较多,也 会选择处理全部数据库 if (dbs_per_call > server.dbnum || timelimit_exit) dbs_per_call = server.dbnum; 执行activeExpireCycle函数的最长时间(以微秒计),其中REDIS_EXPIRELOOKUPS_TIME_PERC 是单位时间内能够分配给activeExpireCycle函数执行的CPU时间比例,默认值为25,server.hz 即为一秒内activeExpireCycle的调用次数,所以这个计算公式更明白的写法应该是这样的,即 (1000000 * (REDIS_EXPIRELOOKUPS_TIME_PERC / 100)) / server.hz timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/server.hz/100; timelimit_exit = 0; if (timelimit <= 0) timelimit = 1; 遍历处理每个Redis数据库中的失效数据 for (j = 0; j < dbs_per_call; j++) { int expired; redisDb *db = server.db+(current_db % server.dbnum); 此处立刻就将current_db加一,这样可以保证即使这次无法在时间限制内删除完所有当前 数据库中的失效主键,下一次调用activeExpireCycle一样会从下一个数据库开始处理, 从而保证每个数据库都有被处理的机会 current_db++; 开始处理当前数据库中的失效主键 do { unsigned long num, slots; long long now; 如果expires字典表大小为0,说明该数据库中没有设置失效时间的主键,直接检查下 一数据库 if ((num = dictSize(db->expires)) == 0) break; slots = dictSlots(db->expires); now = mstime(); 如果expires字典表不为空,但是其填充率不足1%,那么随机选择主键进行检查的代价 会很高,所以这里直接检查下一数据库 if (num && slots > DICT_HT_INITIAL_SIZE && (num*100/slots < 1)) break; expired = 0; 如果expires字典表中的entry个数不足以达到抽样个数,则选择全部key作为抽样样本 if (num > REDIS_EXPIRELOOKUPS_PER_CRON) num = REDIS_EXPIRELOOKUPS_PER_CRON; while (num--) { dictEntry *de; long long t; 随机获取一个设置了失效时间的主键,检查其是否已经失效 if ((de = dictGetRandomKey(db->expires)) == NULL) break; t = dictGetSignedIntegerVal(de); if (now > t) { 发现该主键确实已经失效,删除该主键 sds key = dictGetKey(de); robj *keyobj = createStringObject(key,sdslen(key)); 同样要在删除前广播该主键的失效信息 propagateExpire(db,keyobj); dbDelete(db,keyobj); decrRefCount(keyobj); expired++; server.stat_expiredkeys++; } } 每进行一次抽样删除后对iteration加一,每16次抽样删除后检查本次执行时间是否 已经达到时间限制,如果已达到时间限制,则记录本次执行达到时间限制并退出 iteration++; if ((iteration & 0xf) == 0 && (ustime()-start) > timelimit) { timelimit_exit = 1; return; } 如果失效的主键数占抽样数的百分比大于25%,则继续抽样删除过程 } while (expired > REDIS_EXPIRELOOKUPS_PER_CRON/4); }}
参考文献链接:http://redis.io/commands/expire & http://redis.io/topics/latency &
http://www.cppblog.com/richbirdandy/archive/2011/11/29/161184.html &
http://www.cnblogs.com/tangtianfly/archive/2012/05/02/2479315.html
0 0
- 深入理解Redis中的主键失效及其实现机制
- 深入理解Redis中的主键失效及其实现机制
- 深入理解Redis中的主键失效及其实现机制
- 深入理解Redis中的主键失效及其实现机制
- 深入理解Redis中的主键失效及其实现机制
- 深入理解Redis主键失效原理及实现机制
- 深入理解Redis主键失效原理及实现机制
- 深入理解Redis主键失效原理及实现机制
- 深入理解Redis主键失效原理及实现机制
- 深入理解Redis主键失效原理及实现机制
- 深入理解Redis主键失效原理及实现机制
- 深入理解Redis主键失效原理及实现机制
- 深入理解Redis主键失效原理及实现机制
- Redis的缓存策略和主键失效机制
- Redis的缓存策略和主键失效机制
- Redis的缓存策略和主键失效机制
- Redis 的缓存策略和主键失效机制
- Redis的缓存策略和主键失效机制
- JAVA温习课堂14
- SQL面试题目汇总
- android 调用webservice出错
- 机器学习算法之七:5分钟上手SVM
- sql 查询科目成绩以及平均成绩
- 深入理解Redis中的主键失效及其实现机制
- 【Android实战之旅 004】Android摄像头基础
- Leetcode 283 Move zeros
- 如何查看当前ubuntu的版本信息
- CSS3的calc()使用
- redis 学习笔记(7)-cluster 客户端(jedis)代码示例
- 索引
- 关于Android studio的文件冲突问题
- iOS View的Frame和bounds之区别,setbounds使用(深入探究)