结合redis设计与实现的redis源码学习-25-慢查询日志(slowlog)

Redis的慢查询日志功能用于记录执行时间超过给定时长的命令请求,用户可以通过这个功能产生的日志来监控和优化查询速度。
服务器配置有两个和慢查询日志相关的选项:
-1、slowlog-log-slower-than选项指定执行时间超过多少微妙的命令会被记录到日志上。
-2、slowlog-max-len选项指定服务器最多保存多少条慢查询日志。
服务器使用先进先出的方式保存多条慢查询日志,当达到设置的最大值时,服务器在添加一条新的慢查询日志之前,会先将最旧的一条删除。

一、慢查询记录的保存

struct redisServer{
    long long slowlog_entry_id;//下一条慢查询日志的ID
    list *slowlog;//保存了所有慢查询日志的链表
    long long slowlog_log_slower_than;//服务器配置的保存日志的命令执行时间
    unsigned long slowlog_max_len;//服务器配置的最大日志长度
}

其中链表保存的每个节点都是一个结构体:

typedef struct slowlogEntry{
    long long id;//唯一标识符
    time_t time;//命令执行的时间
    long long duration;//执行命令消耗的时间,以微妙为单位
    robj **argv;//命令与参数
    int argc;//命令与参数的数量
}

二、添加新日志

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

看代码

slowlog.h

#define SLOWLOG_ENTRY_MAX_ARGC 32
#define SLOWLOG_ENTRY_MAX_STRING 128

/* Exported API 导出的API*/
void slowlogInit(void);
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration);
/* Exported commands 导出的命令*/
void slowlogCommand(client *c);

slowlog.c

#include "server.h"
#include "slowlog.h"

/* Create a new slowlog entry.创建一个日志条目
 * Incrementing the ref count of all the objects retained is up to this function. 增加保留的所有对象的引用计数器取决于此功能*/
slowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) {
    slowlogEntry *se = zmalloc(sizeof(*se));
    int j, slargc = argc;

    if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC;
    se->argc = slargc;
    se->argv = zmalloc(sizeof(robj*)*slargc);
    for (j = 0; j < slargc; j++) {
        /* Logging too many arguments is a useless memory waste, so we stop at SLOWLOG_ENTRY_MAX_ARGC, but use the last argument to specify how many remaining arguments there were in the original command. 记录过多的参数是无用的内存浪费,但是我们使用最后一个参数来指定原始命令中剩余的参数数量*/
        if (slargc != argc && j == slargc-1) {
            se->argv[j] = createObject(OBJ_STRING,
                sdscatprintf(sdsempty(),"... (%d more arguments)",
                argc-slargc+1));
        } else {
            /* Trim too long strings as well... 截取太长的字符串*/
            if (argv[j]->type == OBJ_STRING &&
                sdsEncodedObject(argv[j]) &&
                sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING)
            {
                sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING);

                s = sdscatprintf(s,"... (%lu more bytes)",
                    (unsigned long)
                    sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING);
                se->argv[j] = createObject(OBJ_STRING,s);
            } else {
                se->argv[j] = argv[j];
                incrRefCount(argv[j]);
            }
        }
    }
    se->time = time(NULL);
    se->duration = duration;
    se->id = server.slowlog_entry_id++;
    return se;
}
/* Free a slow log entry. The argument is void so that the prototype of this function matches the one of the 'free' method of adlist.c.释放一个日志条目,这个参数是无效的,所以这个函数的原型与adlist.c的free方法中的一个匹配
 This function will take care to release all the retained object. 这个函数小心的释放所有保留的对象*/
void slowlogFreeEntry(void *septr) {
    slowlogEntry *se = septr;
    int j;

    for (j = 0; j < se->argc; j++)
        decrRefCount(se->argv[j]);
    zfree(se->argv);
    zfree(se);
}
/* Initialize the slow log. This function should be called a single time at server startup. 初始化慢查询日志,这个函数只会在服务器启动时调用*/
void slowlogInit(void) {
    server.slowlog = listCreate();
    server.slowlog_entry_id = 0;
    listSetFreeMethod(server.slowlog,slowlogFreeEntry);
}
/* Push a new entry into the slow log.插入一条日志
 This function will make sure to trim the slow log accordingly to the configured max length. 这个函数判断是否需要创建日志*/
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {
    if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled 没有开启慢查询日志,直接返回*/
    if (duration >= server.slowlog_log_slower_than)
        listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));

    /* Remove old entries if needed. 如果超过最大长度,删除最旧的*/
    while (listLength(server.slowlog) > server.slowlog_max_len)
        listDelNode(server.slowlog,listLast(server.slowlog));
}
/* Remove all the entries from the current slow log. 释放所有日志条目*/
void slowlogReset(void) {
    while (listLength(server.slowlog) > 0)
        listDelNode(server.slowlog,listLast(server.slowlog));
}

/* The SLOWLOG command. Implements all the subcommands needed to handle the Redis slow log. 执行慢查询日志的相关命令*/
void slowlogCommand(client *c) {
    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) {
        slowlogReset();
        addReply(c,shared.ok);
    } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) {
        addReplyLongLong(c,listLength(server.slowlog));
    } else if ((c->argc == 2 || c->argc == 3) &&
               !strcasecmp(c->argv[1]->ptr,"get"))
    {
        long count = 10, sent = 0;
        listIter li;
        void *totentries;
        listNode *ln;
        slowlogEntry *se;

        if (c->argc == 3 &&
            getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != C_OK)
            return;

        listRewind(server.slowlog,&li);
        totentries = addDeferredMultiBulkLength(c);
        while(count-- && (ln = listNext(&li))) {
            int j;

            se = ln->value;
            addReplyMultiBulkLen(c,4);
            addReplyLongLong(c,se->id);
            addReplyLongLong(c,se->time);
            addReplyLongLong(c,se->duration);
            addReplyMultiBulkLen(c,se->argc);
            for (j = 0; j < se->argc; j++)
                addReplyBulk(c,se->argv[j]);
            sent++;
        }
        setDeferredMultiBulkLength(c,totentries,sent);
    } else {
        addReplyError(c,
            "Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN.");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值