美文网首页
Redis | 第10章 二进制数组、慢查询日志和监视器《Red

Redis | 第10章 二进制数组、慢查询日志和监视器《Red

作者: 多氯环己烷 | 来源:发表于2021-12-05 16:52 被阅读0次

    前言

    参考资料:《Redis设计与实现 第二版》;

    第三部分为独立功能的实现,主要由以下模块组成:发布订阅事务Lua 脚本排序二进制位数组慢查询日志监视器

    本篇将介绍 Redis 的二进制位数组慢查询日志监视器。Redis 提供了一些命令操作二进制位数组;通过 SLOWLOG 相关命令可以对慢查询日志进行操作;通过 MONITOR 命令可以进入监视器模式;

    与本章相关的 Redis 命令总结在下篇文章,欢迎点击收藏,本篇将不再重复:

    《Redis常用命令及示例总结(API)》https://www.jianshu.com/p/f8eb9afaa908


    1. 二进制位数组

    1.1 位数组的表示

    • Redis 使用字符串对象来表示位数组,因为字符串对象使用的 SDS 是二进制安全的;
    • 使用逆序保存位数组,下图实际为:1111 0000 1100 0011 1010 0101;
      • 使用逆序保存位数组,可以直接在新扩展的二进制位中完成,不必改动位数组原来已有的二进制位;
    三字节长的 SDS 位数组.png

    1.2 GETBIT 命令的实现

    • GETBIT key offset 命令;
    • 1)计算 byte=offset / 8;byte 值表示位数组的哪个字节;
    • 2)计算 bit=(offset mod 8)+1;bit 值表示在 byte 下标字节的第几个二进制位;
    • 3)根据 byte 和 bit 的值定位 offfset 偏移量指定的二进制位;
    • 4)向客户端返回二进制位的值;
    GETBIT 命令示例.png

    1.3 SETBIT 命令的实现

    • SETBIT key offset value 命令;
    • 1)计算 len=offset/8+1;len 值表示 offset 指定的二进制位至少需要多少字节;
    • 2)根据 len 值进行扩展新空间,如果原位数组长度够则不扩展;
    • 3)计算 byte=offset/8;byte 值表示位数组的哪个字节;
    • 4)计算 bit=(offset mod 8)+1;bit 值表示在 byte 下标字节的第几个二进制位;
    • 5)根据 byte 和 bit 的值定位 offfset 偏移量指定的二进制位 oldvalue,并修改;
    • 6)向客户端返回二进制位 oldvalue 的值;
    • 无扩展操作的 SETBIT 命令示例如下:
      SETBIT 命令示例.png
    • 带扩展操作的 SETBIT 命令示例如下:
      SETBIT 命令示例2.png

    1.4 BITECOUNT 命令的实现

    • 遍历算法
      • 遍历位数组中的每个二进制位,遇到 1 时将计数器值赠 1;
      • 实现简单,效率低;
    • 查表法
      • 对于长度有限的位数组而言,能表示的二进制位有限,而每种排列顺序的 1 的个数是确定的;
      • 查表法是典型的空间换时间策略,节约时间越多,花费内存越大
      • 受 CPU 缓存限制:创建表越大,能加载进缓存的内容越少,缓存不命中的情况越高,缓存的换入换出操作就越频繁,最终影响实际效率;


        查表法的表.png
    • variable-precision SWAR 算法
      • 又称:计算汉明重量法;

      • 一个处理 32 位长度位数的算法示例:

        uint32_t swar(unit32_t i){
            //步骤1:按每2个二进制位为一组分组,各组的十进制为汉明重量
            i = (i & 0x55555555) + ((i >> 1) & 0x55555555);
            //步骤2:按每4个二进制位为一组分组,各组的十进制为汉明重量
            i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
            //步骤3:按每8个二进制位为一组分组,各组的十进制为汉明重量
            i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F);
            //步骤4
            i = (i*(0x01010101) >> 24);
            return i;
        }
        
      • 一个示例如下:

    汉明重量示例.png 汉明重量示例2.png
    • Redis 的实现
      • BITCOUNT 命令使用查表法variable-precision SWAR 法两种;
        • 查表法:使用键长为 8 位的表,记录 0000 0000 到 1111 1111 在内的二进制位的汉明重量;
        • variable-precision SWAR 法:每次循环中载入 128 个二进制位,调用 4 次 32 位的variable-precision SWAR 法计算 128 位二进制位的汉明重量;
      • 程序根据未处理的二进制位的数量决定使用哪种算法:
        • 未处理二进制位大于等于 128 位:variable-precision SWAR 法
        • 未处理二进制位小于 128 位:查表法
    • 算法的时间复杂度为:O(n),n 为输入二进制位的数量;

    1.5 BITOP 命令的实现

    • BITOP 命令的所有操作都是使用 C 语言内置的位操作来实现的;

      BITOP 命令 C语言操作 说明
      BITOP AND & 逻辑与
      BITOP OR | 逻辑或
      BITOP XOR ^ 逻辑异或
      BITOP NOT ~ 逻辑非

    2. 慢查询日志

    • Redis 的慢查询日志功能用于记录执行时间超过给定时长的命令请求,用户可以通过这个功能产生的例子来监视和优化查询速度;

    • 服务器配置有两个和慢查询日志相关的选项:

      • slowlog-log-slower-than 选项:指定执行时间超过多少毫秒的命令请求会被记录到日志上;
      • slowlog-max-len 选项:指定服务器最多保存多少条慢查询日志。采用先进先出的方式;
    • 使用 SLOWLOG 相关命令可以操作慢查询日志;

    2.1 慢查询记录的保存

    • 服务器状态中包含与慢查询日志功能相关的属性:

      struct redisServer{
          //...
          // 下一条慢查询日志的 ID
          long long slowlog_entry_id;
          // 保存了所有慢查询日志的链表
          list *slowlog;
          // 服务器配置 slow_log_slower_than 选项的值
          long long slow_log_slower_than;
          // 服务器配置 slowlog_max_len 选项的值
          unsigned long slowlog_max_len;
      };
      
    慢查询逻辑图.png
    • slowlog 链表结构如下:

      typedef struct slowlogEntry{
          // 唯一标识符
          long long id;
          // 命令执行时的时间,格式为 UNIX 时间戳
          time_t time;
          // 执行命令消耗的时间,以微秒为单位
          long long duration;
          // 命令与命令参数
          robj **argv;
          // 命令与命令参数的数量
          int argc;
      } slowlogEntry;
      
    慢查询链表示例.png

    2.2 慢查询日志的阅览与删除

    • SLOWLOG GET [number];打印所有 slow log ,最大长度取决于 slowlog-max-len 选项的值;
    • SLOWLOG LEN;查看当前日志的数量。其值为 slowlog 链表的长度;
    • SLOWLOG RESET;清除所有慢查询日志;

    2.3 添加新日志

    • 每次执行命令的之前和之后,程序会记录微秒格式的当前 UNIX 时间戳,两个时间戳之间的差值就是服务器执行命令所消耗的时长;
    • 新的慢查询日志会被添加到 slowlog 链表的表头,如果日志的数量超过 slowlog-max-len 选项的值,那么多出来的日志会被删除;

    3. 监视器

    • 执行 MONITOR 命令,客户端可以将自己变为一个监视器,实时打印服务器当前处理的命令请求相关信息;
    • 当一个客户端从普通客户端变为监视器时,该客户端的 REDIS_MONITOR 标识会被打开,该客户端会被添加到 monitors 链表的表尾;
    • 所有监视器都记录在 monitors 链表里;
    • 每次处理命令请求时,服务器会遍历 monitors 链表,将相关信息发送给监视器;
    监视器.png

    最后

    \color{blue}{\rm\small{新人制作,如有错误,欢迎指出,感激不尽!}}

    \color{blue}{\rm\small{欢迎关注我,并与我交流!}}

    \color{blue}{\rm\small{如需转载,请标注出处!}}

    相关文章

      网友评论

          本文标题:Redis | 第10章 二进制数组、慢查询日志和监视器《Red

          本文链接:https://www.haomeiwen.com/subject/qpzixrtx.html