【Redis实现系列】慢查询日志实现

慢查询日志

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

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

  • slowlog-log-slower-than 选项指定执行时间超过多少微秒(1秒等于1 000 000微秒)的命令请求会被记录到日志上。

  • slowlog-max-len选项指定服务器最多保存多少条慢查询日志。 服务器使用先进先出的方式保存多条慢查询日志,当服务器存储的慢查询日志数量等于slowlog-max-len选项的值时,服务器在添加一条新的慢查询日志之前,会先将最旧的一条慢查询日志删除。

使用SLOWLOG GET命令查看服务器所保存的慢查询日志

保存实现

服务器状态中包含了几个和慢查询日志功能有关的属性:

struct redisServer {
    // ...
    // 下一条慢查询日志的ID
    // 初始值为0,每当创建一条新的慢查询日志时,这个属性的值就会用作新日志的id值,之后程序会对这个属性的值增一。
    long long slowlog_entry_id;
    // 保存了所有慢查询日志的链表
    list *slowlog;
    // 服务器配置slowlog-log-slower-than选项的值
    long long slowlog_log_slower_than;
    // 服务器配置slowlog-max-len选项的值
    unsigned long slowlog_max_len;
    // ...
};

slowlog 链表保存了服务器中的所有慢查询日志

  • 链表中的每个节点都保存了一个slowlogEntry结构,每个slowlogEntry结构代表一条慢查询日志:
typedef struct slowlogEntry {
    // 唯一标识符
    long long id;
    // 命令执行时的时间,格式为UNIX时间戳
    time_t time;
    // 执行命令消耗的时间,以微秒为单位
    long long duration;
    // 命令与命令参数
    robj **argv;
    // 命令与命令参数的数量
    int argc;
} slowlogEntry;
  • 示例:日志所对应的slowlogEntry结构
1) (integer) 5
2) (integer) 1378781521
3) (integer) 61
4) 1) "GET
   2) "number"

在这里插入图片描述

添加

在每次执行命令的之前和之后,程序都会记录微秒格式的当前UNIX时间戳,这两个时间戳之间的差就是服务器执行命令所耗费的时长

  • 服务器会将这个时长作为参数之一传给slowlogPushEntryIfNeeded函数,而slowlogPushEntryIfNeeded函数则负责检查是否需要为这次执行的命令创建慢查询日志,以下伪代码展示了这一过程:
# 记录执行命令前的时间
before = unixtime_now_in_us()
# 执行命令
execute_command(argv, argc, client)
# 记录执行命令后的时间
after = unixtime_now_in_us()
# 检查是否需要创建新的慢查询日志
slowlogPushEntryIfNeeded(argv, argc, before-after)

slowlogPushEntryIfNeeded函数的实现代码:

void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {
    // 慢查询功能未开启,直接返回
    if (server.slowlog_log_slower_than < 0) return;
    // 如果执行时间超过服务器设置的上限,那么将命令添加到慢查询日志
    if (duration >= server.slowlog_log_slower_than)
        // 新日志添加到链表表头
        listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));
    // 如果日志数量过多,那么进行删除
    while (listLength(server.slowlog) > server.slowlog_max_len)
        listDelNode(server.slowlog,listLast(server.slowlog));
}

阅览和删除

阅览

  • SLOWLOG GET

    def SLOWLOG_GET(number=None):
        # 用户没有给定number参数
        # 那么打印服务器包含的全部慢查询日志
        if number is None:
            number = SLOWLOG_LEN()
        # 遍历服务器中的慢查询日志
        for log in redisServer.slowlog:
            if number <= 0:
                # 打印的日志数量已经足够,跳出循环
                break
            else:
                # 继续打印,将计数器的值减一
                number -= 1
            # 打印日志
            printLog(log)
    
  • SLOWLOG LEN

    def SLOWLOG_LEN():
        # slowlog链表的长度就是慢查询日志的条目数量
        return len(redisServer.slowlog)
    

删除

  • SLOWLOG RESET

    def SLOWLOG_RESET():
        # 遍历服务器中的所有慢查询日志
        for log in redisServer.slowlog:
            # 删除日志
            deleteLog(log)
    

小结

Redis的慢查询日志功能用于记录执行时间超过指定时长的命令。

  • Redis服务器将所有的慢查询日志保存在服务器状态的slowlog链表中,每个链表节点都包含一个slowlogEntry结构,每个slowlogEntry结构代表一条慢查询日志。

新的慢查询日志会被添加到slowlog链表的表头,如果日志的数量超过slowlog-max-len选项的值,那么多出来的日志会被删除。

打印和删除慢查询日志可以通过遍历slowlog链表来完成。

  • slowlog链表的长度就是服务器所保存慢查询日志的数量。
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:C马雯娟 返回首页