设为首页 收藏本站
查看: 1186|回复: 0

[经验分享] redis源码笔记

[复制链接]

尚未签到

发表于 2015-7-20 09:27:13 | 显示全部楼层 |阅读模式
  serverCron是redis每隔100ms执行的一个循环事件,由ae事件框架驱动。其主要执行如下任务:
  1.记录循环时间:



server.unixtime = time(NULL)
  redis使用全局状态cache了当前的时间值。在vm实现以及lru实现中,均需要对每一个对象的访问记录其时间,在这种情况下,对精度的要求并不高(100ms内的访问值一样是没有问题的)。使用cache的时间值,其代价要远远低于每次均调用time()系统调用
  2.更新LRUClock值:



updateLRUClock()
  后续在执行lru淘汰策略时,作为比较的基准值。redis默认的时间精度是10s(#define REDIS_LRU_CLOCK_RESOLUTION 10),保存lru clock的变量共有22bit。换算成总的时间为1.5 year(每隔1.5年循环一次)。
  不知为何在最初设计的时候,为lru clock只给了22bit的空间。
  3.更新峰值内存占用:



550     if (zmalloc_used_memory() > server.stat_peak_memory)
551         server.stat_peak_memory = zmalloc_used_memory();
  4.处理shutdown_asap
  在上一篇blog中,介绍了redis对SIG_TERM信号的处理。其信号处理函数中并没有立即终止进程的执行,而是选择了标记shutdown_asap flag,然后在serverCron中通过执行prepareForShutdown函数,优雅的退出。



555     if (server.shutdown_asap) {
556         if (prepareForShutdown() == REDIS_OK) exit(0);
557         redisLog(REDIS_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information");
558     }
  在prepareForShutdown函数中,redis处理了rdb、aof记录文件退出的情况,最后保存了一次rdb文件,关闭了相关的文件描述符以及删除了保存pid的文件(server.pidfile).
  5.打印统计信息
  统计信息分为两类,两类统计信息均为每5s输出一次。第一类是key数目、设置了超时值的key数目、以及当前的hashtable的槽位数:



561     for (j = 0; j < server.dbnum; j++) {
562         long long size, used, vkeys;
563
564         size = dictSlots(server.db[j].dict);
565         used = dictSize(server.db[j].dict);
566         vkeys = dictSize(server.db[j].expires);
567         if (!(loops % 50) && (used || vkeys)) {
568             redisLog(REDIS_VERBOSE,"DB %d: %lld keys (%lld volatile) in %lld slots HT.",j,used,vkeys,size);
569             /* dictPrintStats(server.dict); */
570         }
571     }
  第二类是当前的client数目,slaves数目,以及总体的内存使用情况:



585     if (!(loops % 50)) {
586         redisLog(REDIS_VERBOSE,"%d clients connected (%d slaves), %zu bytes in use",
587             listLength(server.clients)-listLength(server.slaves),
588             listLength(server.slaves),
589             zmalloc_used_memory());
590     }
  6.尝试resize hash表
  因为现在的操作系统fork进程均大多数采用的是copy-on-write,为了避免resize哈希表造成的无谓的页面拷贝,在有后台的rdb save进程或是rdb rewrite进程时,不会尝试resize哈希表。
  否则,将会每隔1s,进行一次resize哈希表的尝试;同时,如果设置了递增式rehash(redis默认是设置的),每次serverCron执行,均会尝试执行一次递增式rehash操作(占用1ms的CPU时间);



579     if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1) {
580         if (!(loops % 10)) tryResizeHashTables();
581         if (server.activerehashing) incrementallyRehash();
582     }
  7.关闭超时的客户端
  每隔10s进行一次尝试



593     if ((server.maxidletime && !(loops % 100)) || server.bpop_blocked_clients)
594         closeTimedoutClients();
  8.如果用户在此期间,请求进行aof的rewrite操作,调度执行rewrite



598     if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1 &&
599         server.aofrewrite_scheduled)
600     {      
601         rewriteAppendOnlyFileBackground();
602     }     
  9.如果有后台的save rdb操作或是rewrite操作:
  调用wait3获取子进程状态。此wait3为非阻塞(设置了WNOHANG flag)。注意:APUE2在进程控制章节其实挺不提倡用wait3和wait4接口的,不过redis的作者貌似对这个情有独钟。如果后台进程刚好退出,调用backgroundSaveDoneHandler或backgroundRewriteDoneHandler进行必要的善后工作,并更新dict resize policy(如果已经没有后台进程了,就可以允许执行resize操作了)。



605     if (server.bgsavechildpid != -1 || server.bgrewritechildpid != -1) {
606         int statloc;
607         pid_t pid;
608     
609         if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
610             if (pid == server.bgsavechildpid) {
611                 backgroundSaveDoneHandler(statloc);
612             } else {
613                 backgroundRewriteDoneHandler(statloc);
614             }
615             updateDictResizePolicy();
616         }
  10.否则,如果没有后台的save rdb操作及rewrite操作:
  首先,根据saveparams规定的rdb save策略,如果满足条件,执行后台rdbSave操作;
  其次,根据aofrewrite策略,如果当前aof文件增长的规模,要求触发rewrite操作,则执行后台的rewrite操作。



622          for (j = 0; j < server.saveparamslen; j++) {
623             struct saveparam *sp = server.saveparams+j;
624         
625             if (server.dirty >= sp->changes &&
626                 now-server.lastsave > sp->seconds) {
627                 redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",
628                     sp->changes, sp->seconds);
629                 rdbSaveBackground(server.dbfilename);
630                 break;
631             }
632          }      
633
634          /* Trigger an AOF rewrite if needed */
635          if (server.bgsavechildpid == -1 &&
636              server.bgrewritechildpid == -1 &&
637              server.auto_aofrewrite_perc &&
638              server.appendonly_current_size > server.auto_aofrewrite_min_size)
639          {
640             long long base = server.auto_aofrewrite_base_size ?
641                             server.auto_aofrewrite_base_size : 1;
642             long long growth = (server.appendonly_current_size*100/base) - 100;
643             if (growth >= server.auto_aofrewrite_perc) {
644                 redisLog(REDIS_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);
645                 rewriteAppendOnlyFileBackground();
646             }
647         }
  11.如果推迟执行aof flush,则进行flush操作,调用flushAppendOnlyFile函数;
  12.如果此redis instance为master,则调用activeExpireCycle,对过期值进行处理(slave只等待master的DEL,保持slave和master的严格一致);
  13.最后,每隔1s,调用replicationCron,执行与replication相关的操作。
  
  在blog的最后,对serverCron的开头结尾进行简单的探讨;
  serverCron开头,有这样几行代码:



525     REDIS_NOTUSED(eventLoop);
526     REDIS_NOTUSED(id);
527     REDIS_NOTUSED(clientData);
  表明,这个时间处理例程内部,对aeCreateTimeEvent规定的函数原型所传的参数,均没有使用。redis的ae库据作者所说,是参考libevent的实现精简再精简得到的,猜测其接口的设计也是借鉴了很多libevent的接口设计风格。
  serverCron最后,return 100。表明server将会在100ms后重新调用这个例程的执行。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-88504-1-1.html 上篇帖子: 四.redis 事务 下篇帖子: Windows系统下Redis的安装
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表