Redis 实战 之慢查询日志功能

慢查询记录的保存

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

struct redisServer {

    // ...

    // 下一条慢查询日志的 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_entry_id 属性的初始值为 0 , 每当创建一条新的慢查询日志时, 这个属性的值就会用作新日志的 id 值, 之后程序会对这个属性的值增一。

比如说, 在创建第一条慢查询日志时, slowlog_entry_id 的值 0 会成为第一条慢查询日志的 ID , 而之后服务器会对这个属性的值增一; 当服务器再创建新的慢查询日志的时候, slowlog_entry_id 的值 1 就会成为第二条慢查询日志的 ID , 然后服务器再次对这个属性的值增一, 以此类推。

slowlog 链表保存了服务器中的所有慢查询日志, 链表中的每个节点都保存了一个 slowlogEntry 结构, 每个 slowlogEntry 结构代表一条慢查询日志:

typedef struct slowlogEntry {

    // 唯一标识符
    long long id;

    // 命令执行时的时间,格式为 UNIX 时间戳
    time_t time;

    // 执行命令消耗的时间,以微秒为单位
    long long duration;

    // 命令与命令参数
    robj **argv;

    // 命令与命令参数的数量
    int argc;

} slowlogEntry;

举个例子, 对于以下慢查询日志来说:

1) (integer) 3
2) (integer) 1378781439
3) (integer) 10
4) 1) "SET"
   2) "number"
   3) "10086"

图23-1 展示的就是该日志所对应的 slowlogEntry 结构。
在这里插入图片描述
在这里插入图片描述

图23-2 展示了服务器状态中, 和慢查询功能有关的属性:

slowlog_entry_id 的值为 6 , 表示服务器下条慢查询日志的 id 值将为 6 。
slowlog 链表包含了 id 为 51 的慢查询日志, 最新的 5 号日志排在链表的表头, 而最旧的 1 号日志排在链表的表尾, 这表明slowlog 链表是使用插入到表头的方式来添加新日志的。
slowlog_log_slower_than 记录了服务器配置 slowlog-log-slower-than 选项的值 0 , 表示任何执行时间超过 0 微秒的命令都会被慢查询日志记录。
slowlog-max-len 属性记录了服务器配置 slowlog-max-len 选项的值 5 , 表示服务器最多储存五条慢查询日志。

注意 因为版面空间不足的缘故, 所以图 23-2 展示的各个 slowlogEntry 结构都省略了 argv 数组。

慢查询日志的阅览和删除

弄清楚了服务器状态的 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)

添加新日志

在每次执行命令的之前和之后, 程序都会记录微秒格式的当前 UNIX 时间戳, 这两个时间戳之间的差就是服务器执行命令所耗费的时长, 服务器会将这个时长作为参数之一传给 slowlogPushEntryIfNeeded 函数, 而 slowlogPushEntryIfNeeded 函数则负责检查是否需要为这次执行的命令创建慢查询日志, 以下伪代码展示了这一过程:

# 记录执行命令前的时间
before = unixtime_now_in_us()

# 执行命令
execute_command(argv, argc, client)

# 记录执行命令后的时间
after = unixtime_now_in_us()

# 检查是否需要创建新的慢查询日志
slowlogPushEntryIfNeeded(argv, argc, before-after)

slowlogPushEntryIfNeeded 函数的作用有两个:

1、 检查命令的执行时长是否超过slowlog-log-slower-than选项所设置的时间,如果是的话,就为命令创建一个新的日志,并将新日志添加到slowlog链表的表头;
2、 检查慢查询日志的长度是否超过slowlog-max-len选项所设置的长度,如果是的话,那么将多出来的日志从slowlog链表中删除掉;

以下是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));
}

函数中的大部分代码我们已经介绍过了, 唯一需要说明的是 slowlogCreateEntry 函数: 该函数根据传入的参数, 创建一个新的慢查询日志, 并将 redisServer.slowlog_entry_id 的值增一。

举个例子, 假设服务器当前保存的慢查询日志如图 23-2 所示, 如果我们执行以下命令:

redis> EXPIRE msg 10086
(integer) 1

服务器在执行完这个 EXPIRE 命令之后, 就会调用 slowlogPushEntryIfNeeded 函数, 函数将为 EXPIRE 命令创建一条 id 为 6 的慢查询日志, 并将这条新日志添加到 slowlog 链表的表头, 如图 23-3 所示。
在这里插入图片描述
注意,除了 slowlog 链表发生了变化之外, slowlog_entry_id 的值也从 6 变为 7 了。

之后,slowlogPushEntryIfNeeded 函数发现, 服务器设定的最大慢查询日志数目为 5 条, 而服务器目前保存的慢查询日志数目为 6 条, 于是服务器将 id 为 1 的慢查询日志删除, 让服务器的慢查询日志数量回到设定好的 5 条。

删除操作执行之后的服务器状态如图 23-4 所示。
在这里插入图片描述

总结

Redis 的慢查询日志功能用于记录执行时间超过指定时长的命令。
Redis 服务器将所有的慢查询日志保存在服务器状态的 slowlog 链表中, 每个链表节点都包含一个 slowlogEntry 结构, 每个slowlogEntry 结构代表一条慢查询日志。
打印和删除慢查询日志可以通过遍历 slowlog 链表来完成。
slowlog 链表的长度就是服务器所保存慢查询日志的数量。
新的慢查询日志会被添加到 slowlog 链表的表头, 如果日志的数量超过 slowlog-max-len 选项的值, 那么多出来的日志会被删除。
  • 23
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值