Redis源码分析:serverCron - redis源码笔记

【http://www.cnblogs.com/liuhao/archive/2012/06/06/2538751.html】
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.更新峰值内存占用:
550if (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函数,优雅的退出。
555if (server.shutdown_asap) {
556if (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的槽位数:

561for (j = 0; j < server.dbnum; j++) {
562longlong size, used, vkeys;
563564 size = dictSlots(server.db[j].dict);
565 used = dictSize(server.db[j].dict);
566 vkeys = dictSize(server.db[j].expires);
567if (!(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数目,以及总体的内存使用情况:

585if (!(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时间);
579if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1) {
580if (!(loops % 10)) tryResizeHashTables();
581if (server.activerehashing) incrementallyRehash();
582 }
7.关闭超时的客户端
每隔10s进行一次尝试
593if ((server.maxidletime && !(loops % 100)) || server.bpop_blocked_clients)
594 closeTimedoutClients();
8.如果用户在此期间,请求进行aof的rewrite操作,调度执行rewrite
598if (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操作了)。

605if (server.bgsavechildpid != -1 || server.bgrewritechildpid != -1) {
606int statloc;
607 pid_t pid;
608609if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
610if (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操作。

622for (j = 0; j < server.saveparamslen; j++) {
623struct saveparam *sp = server.saveparams+j;
624625if (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);
630break;
631 }
632 }
633634/* Trigger an AOF rewrite if needed */635if (server.bgsavechildpid == -1 &&
636 server.bgrewritechildpid == -1 &&
637 server.auto_aofrewrite_perc &&
638 server.appendonly_current_size > server.auto_aofrewrite_min_size)
639 {
640longlongbase = server.auto_aofrewrite_base_size ?
641 server.auto_aofrewrite_base_size : 1;
642longlong growth = (server.appendonly_current_size*100/base) - 100;
643if (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后重新调用这个例程的执行。
Redis源码分析:serverCron - redis源码笔记的更多相关文章
- NIO 源码分析(05) Channel 源码分析
目录 一.Channel 类图 二.begin 和 close 是什么 2.1 AbstractInterruptibleChannel 中的 begin 和 close 2.2 Selector 中 ...
- NIO 源码分析(02-2) BIO 源码分析 Socket
目录 一.BIO 最简使用姿势 二.connect 方法 2.1 Socket.connect 方法 2.2 AbstractPlainSocketImpl.connect 方法 2.3 DualSt ...
- NIO 源码分析(02-1) BIO 源码分析
目录 一.BIO 最简使用姿势 二.ServerSocket 源码分析 2.1 相关类图 2.2 主要属性 2.3 构造函数 2.4 bind 方法 2.5 accept 方法 2.6 总结 NIO ...
- [源码分析] 从实例和源码入手看 Flink 之广播 Broadcast
[源码分析] 从实例和源码入手看 Flink 之广播 Broadcast 0x00 摘要 本文将通过源码分析和实例讲解,带领大家熟悉Flink的广播变量机制. 0x01 业务需求 1. 场景需求 对黑 ...
- drf的基本使用、APIView源码分析和CBV源码拓展
cbv源码拓展 扩展,如果我在Book视图类中重写dispatch方法 -可以实现,在get,post方法执行之前或者之后执行代码,完成类似装饰器的效果 def dispatch(self, requ ...
- Spring Ioc源码分析系列--Ioc源码入口分析
Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...
- k8s client-go源码分析 informer源码分析(3)-Reflector源码分析
k8s client-go源码分析 informer源码分析(3)-Reflector源码分析 1.Reflector概述 Reflector从kube-apiserver中list&watc ...
- jQuery1.11源码分析(1)-----Sizzle源码概览[原创]
最近在啃jQuery1.11源码,上来就遇到Sizzle这个jQuery的大核心,虽然已经清楚了Sizzle的用途,先绕过去也没事,但明知山有虎偏向虎山行才是我们要做的. 本文面向的阅读对象:正在学习 ...
- 【MyBatis源码分析】select源码分析及小结
示例代码 之前的文章说过,对于MyBatis来说insert.update.delete是一组的,因为对于MyBatis来说它们都是update:select是一组的,因为对于MyBatis来说它就是 ...
- 集合源码分析[3]-ArrayList 源码分析
历史文章: Collection 源码分析 AbstractList 源码分析 介绍 ArrayList是一个数组队列,相当于动态数组,与Java的数组对比,他的容量可以动态改变. 继承关系 Arra ...
随机推荐
- MySQL中myisam和innodb的主键索引有什么区别?
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址.下图是MyISAM索引的原理图: 这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索 ...
- 我的Android进阶之旅------>Android关于dp(dip)、sp转px的工具类
下面是一个工具类,提供了dp.sp.px之间相互转化的方法. import android.content.Context; /** * dp.sp 转换为 px 的工具类<br> * & ...
- Log4j详细配置解释
原文地址:https://www.cnblogs.com/godtrue/p/6444158.html log4j(七)——log4j.xml简单配置样例说明 一:测试环境与log4j(一)——为什么 ...
- oracle11g参数的简单查看方法
1.查看processes和sessions参数show parameter processesshow parameter sessions 2.修改processes和sessions值alter ...
- 佳能相机操作 EDSDK 教程 C# 版本
http://blog.csdn.net/zajin/article/details/17021339 介绍 佳能EOS数码SDK是一个 可以用来远程控制其数码单反相机相当强大的SDK.不幸的是 ...
- LeetCode:路径总和【112】
LeetCode:路径总和[112] 题目描述 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例 ...
- DOM加载过程
静态的dom 动态的dom http://blog.csdn.net/cxiaokai/article/details/7552653 一:预编译 解释 js加 ...
- Kattis - sortofsorting 【排序】
题意 给出一系列字符串,然后要排序 排序规则 只按前两位按字典序来排序,如果前两位完全一样,则按输入的顺序来排 思路 要用 冒泡排序 不能用STL里面的 SORT 因为它不稳定 AC代码 #inclu ...
- Linux文件系统管理 parted分区命令
概述 parted 命令是可以在命令行直接分区和格式化的,不过 parted 交互模式才是更加常用的命令方式. parted命令 进入交互模式命令如下: [root@localhost ~]# par ...
- Android开发之旅-Fragment和Activity之间onCreateOptionsMenu的联系
Fragment和Activity一样,可以重写onCreateOptionsMenu方法来设定自己的菜单,其实这两个地方使用onCreateOptionsMenu的目的和效果都是完全一样的,但是由于 ...