• REDIS源码中一些值得学习的技术细节01


    redis.c/exitFromChild函数:

    1 void exitFromChild(int retcode) {
    2 #ifdef COVERAGE_TEST
    3     exit(retcode);
    4 #else
    5     _exit(retcode);
    6 #endif
    7 }

    分了两种情况考虑,如果是执行完RDB dump, AOF rewrite这类退出操作,为防止影响到父进程文件(父进程子进程共享文件描述符, exit函数会刷新子进程拷贝的stdio缓冲区副本),所以调用_exit()函数
    但如果是在做coverage test, 为了得到正确的coverage信息,那就应该调用exit()来先执行退出处理程序,刷新stdio流缓冲区,再终止进程

    关于exit()与_exit()区别,更具体的信息可参考《The Linux Programming Interface》chapter25.4 以及这个stackoverflow上的问题

    http://stackoverflow.com/questions/5422831/what-is-the-difference-between-using-exit-exit-in-a-conventional-linux-fo

    redis.c/activeExpireCycle函数:

     1 void activeExpireCycle(void) {
     2     int j, iteration = 0;
     3     long long start = ustime(), timelimit;
     4 
     5     /* We can use at max REDIS_EXPIRELOOKUPS_TIME_PERC percentage of CPU time
     6      * per iteration. Since this function gets called with a frequency of
     7      * REDIS_HZ times per second, the following is the max amount of
     8      * microseconds we can spend in this function. */
     9 
    10     timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/REDIS_HZ/100;
    11     if (timelimit <= 0) timelimit = 1;
    12 
    13     for (j = 0; j < server.dbnum; j++) {
    14         int expired;
    15         redisDb *db = server.db+j;
    16 
    17         /* Continue to expire if at the end of the cycle more than 25%
    18          * of the keys were expired. */
    19         do {
    20             unsigned long num = dictSize(db->expires);
    21             unsigned long slots = dictSlots(db->expires);
    22             long long now = mstime();
    23 
    24             /* When there are less than 1% filled slots getting random
    25              * keys is expensive, so stop here waiting for better times...
    26              * The dictionary will be resized asap. */
    27             // 过期字典里只有 %1 位置被占用,调用随机 key 的消耗比较高
    28             // 等 key 多一点再来
    29             if (num && slots > DICT_HT_INITIAL_SIZE &&
    30                 (num*100/slots < 1)) break;
    31 
    32             /* The main collection cycle. Sample random keys among keys
    33              * with an expire set, checking for expired ones. */
    34             // 从过期字典中随机取出 key ,检查它是否过期
    35             expired = 0;    // 被删除 key 计数
    36             if (num > REDIS_EXPIRELOOKUPS_PER_CRON) // 最多每次可查找的次数
    37                 num = REDIS_EXPIRELOOKUPS_PER_CRON;
    38             while (num--) {
    39                 dictEntry *de;
    40                 long long t;
    41 
    42                 // 随机查找带有 TTL 的 key ,看它是否过期
    43                 // 如果数据库为空,跳出
    44                 if ((de = dictGetRandomKey(db->expires)) == NULL) break;
    45 
    46                 t = dictGetSignedIntegerVal(de);
    47                 if (now > t) {
    48                     // 已过期
    49                     sds key = dictGetKey(de);
    50                     robj *keyobj = createStringObject(key,sdslen(key));
    51 
    52                     propagateExpire(db,keyobj);
    53                     dbDelete(db,keyobj);
    54                     decrRefCount(keyobj);
    55                     expired++;
    56                     server.stat_expiredkeys++;
    57                 }
    58             }
    59             /* We can't block forever here even if there are many keys to
    60              * expire. So after a given amount of milliseconds return to the
    61              * caller waiting for the other active expire cycle. */
    62             // 每次进行 16 次循环之后,检查时间是否超过,如果超过,则退出
    63             iteration++;
    64             if ((iteration & 0xf) == 0 && /* check once every 16 cycles. */
    65                 (ustime()-start) > timelimit) return;
    66 
    67         } while (expired > REDIS_EXPIRELOOKUPS_PER_CRON/4);
    68     }
    69 }

    此函数作为cron函数,按照源码设定,需要在0.0025秒的时间内尽可能多的清除过期键,所以函数执行的效率极为关键。
    这里面,如第64行所示, 对每个数据库检查过期键,在每经过16次循环后,需要检查时间是否超时,设iteration为循环计数
    我的第一想法,是if(iteration%16 == 0), 但redis却使用了 if((iteration & 0xf) == 0) 的位运算方法,为什么要用这么“装逼”的写法呢?
    究其原因,是因为位运算远快于取余运算,在处理高并发场景时,函数执行的时间尤为关键,而此函数作为cron函数,需要在0.0025秒的时间内尽可能多的清除过期键,
    采取这种位运算方式,自然节省了大量时间用于查找和清除过期键。这也提示我们,在对运行时间要求十分苛刻的场合,要善用位运算。

  • 相关阅读:
    Tomcat自动部署
    java环境配置
    django-crispy-forms入门指南
    hibernate级联删除
    bzoj1659: [Usaco2006 Mar]Lights Out 关灯
    bzoj1658: [Usaco2006 Mar]Water Slides 滑水
    bzoj5470 / P4578 [FJOI2018]所罗门王的宝藏(差分约束)
    P2864 [USACO06JAN]树林The Grove
    bzoj1651 / P2859 [USACO06FEB]摊位预订Stall Reservations
    bzoj1647 / P1985 [USACO07OPEN]翻转棋
  • 原文地址:https://www.cnblogs.com/viggoxskingdom/p/4989669.html
Copyright © 2020-2023  润新知