redis源码浅析--二十一.慢查询日志的实现

本文详细介绍了 Redis 中慢查询日志的实现,包括慢查询记录的保存、获取与删除。通过分析 `redisServer` 结构体中的 `slowlog` 属性和 `slowlogCommand` 命令的实现,展示了如何记录和管理执行时间超过预设阈值的命令。此外,还解释了 `slowlogPushEntryIfNeeded` 函数如何根据配置动态添加新的慢查询日志,并保持列表长度不超过设定的最大值。
摘要由CSDN通过智能技术生成

环境说明:redis源码版本 5.0.3;我在阅读源码过程做了注释,git地址:https://gitee.com/xiaoangg/redis_annotation
如有错误欢迎指正
参考书籍:《redis的设计与实现》

文章推荐:
redis源码阅读-一--sds简单动态字符串
redis源码阅读--二-链表
redis源码阅读--三-redis散列表的实现
redis源码浅析--四-redis跳跃表的实现
redis源码浅析--五-整数集合的实现
redis源码浅析--六-压缩列表
redis源码浅析--七-redisObject对象(下)(内存回收、共享)
redis源码浅析--八-数据库的实现
redis源码浅析--九-RDB持久化
redis源码浅析--十-AOF(append only file)持久化
redis源码浅析--十一.事件(上)文件事件
redis源码浅析--十一.事件(下)时间事件
redis源码浅析--十二.单机数据库的实现-客户端
redis源码浅析--十三.单机数据库的实现-服务端 - 时间事件
redis源码浅析--十三.单机数据库的实现-服务端 - redis服务器的初始化
redis源码浅析--十四.多机数据库的实现(一)--新老版本复制功能的区别与实现原理
redis源码浅析--十四.多机数据库的实现(二)--复制的实现SLAVEOF、PSYNY
redis源码浅析--十五.哨兵sentinel的设计与实现
redis源码浅析--十六.cluster集群的设计与实现
redis源码浅析--十七.发布与订阅的实现
redis源码浅析--十八.事务的实现
redis源码浅析--十九.排序的实现
redis源码浅析--二十.BIT MAP的实现
redis源码浅析--二十一.慢查询日志的实现
redis源码浅析--二十二.监视器的实现

目录

一 慢查询记录的保存

二 慢查询日志的获取和删除

三 添加新日志


redis的慢查询日志用于记录执行时间查过给定时长的命令请求;我们可以用慢查询日志来监视和优化查询速度;

相关配置项

  • slowlog-log-slower-than 执行时间超过 slowlog-log-slower-than 微秒的命令 会记录到慢查询日志中
  • slowlog-max-len 指定服务器最多保存多少条慢查询日志

查看慢日志
使用slowlog get命令可以查看服务器保存的慢查询日志

 

一 慢查询记录的保存

服务器状态中slowlog* 记录了和慢查询功能相关的属性;

server.h/redisServer 

 struct redisServer {
    //.............
     /**
     * slowlog 慢查询日志相关
     */ 
    list *slowlog;                  /* SLOWLOG list of commands */ //慢查询日志列表
    long long slowlog_entry_id;     /* SLOWLOG current entry ID */ //下一条慢查询日志id
    long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */ /**
                                                                                * 服务配置的slowlog_log_slower_than 
                                                                                *(执行时间超过 slowlog-log-slower-than 
                                                                                *微秒的命令 会记录到慢查询日志中)
                                                                                */
    unsigned long slowlog_max_len;     /* SLOWLOG max number of items logged */ //服务配置的slowlog_max_len (服务器最大保存日志条数)

    //............
}
    

redisServer->slowlog 属性是一个列表,而列表中存储的值则是slowlog.h/slowlogEntry结构体类型的日志

/**
 * 慢日志列表中一个条目的定义
 */ 
/* This structure defines an entry inside the slow log list */
typedef struct slowlogEntry {
    robj **argv;
    int argc;
    long long id;       /* Unique entry identifier. */
    long long duration; /* Time spent by the query, in microseconds. */
    time_t time;        /* Unix time at which the query was executed. */
    sds cname;          /* Client name. */
    sds peerid;         /* Client network address. */
} slowlogEntry;

 

二 慢查询日志的获取和删除

slowlog get命令可以获取慢查询日志, 默认获取列表的前10条;
语法:

SLOWLOG GET [count]

slowlog 命令的实现位于slowlog.c/slowlogCommand



/**
 * slowlog 命令。实现处理Redis慢日志所需的所有子命令。
 */ 
/* 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,"help")) {
        const char *help[] = {
"GET [count] -- Return top entries from the slowlog (default: 10)."
"    Entries are made of:",
"    id, timestamp, time in microseconds, arguments array, client IP and port, client name",
"LEN -- Return the length of the slowlog.",
"RESET -- Reset the slowlog.",
NULL
        };
        addReplyHelp(c, help);
    } else 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"))
    { //slowlog get命令实现

        /**
         * count 默认获取10条
         */ 
        long count = 10, sent = 0;
        listIter li; //迭代器
        void *totentries;
        listNode *ln;
        slowlogEntry *se;

        //获取 命令行中 count参数(获取条数)
        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,6);
            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]);
            addReplyBulkCBuffer(c,se->peerid,sdslen(se->peerid));
            addReplyBulkCBuffer(c,se->cname,sdslen(se->cname));
            sent++;
        }

        setDeferredMultiBulkLength(c,totentries,sent);
    } else {
        addReplySubcommandSyntaxError(c);
    }
}

 

三 添加新日志

redis每次执行命令前后,程序都会记录微妙格式的当前unix时间戳,用于计算命令的执行时间。

然后服务器如果开启慢查询日志,会将这个时间传递给slowlog.c/slowlogPushEntryIfNeeded, 负责检查是否需要记录慢查询

命令的执行的核心代码位于serverl.c/call

void call(client *c, int flags) {
      
    //....................

    /* Call the command. */
    dirty = server.dirty;
    start = ustime(); //命令开始执行时间
    c->cmd->proc(c); //执行命名
    duration = ustime()-start;//命令结束时执行时间
    dirty = server.dirty-dirty;
    if (dirty < 0) dirty = 0;

    //..................
    /**
     * 如果开启了慢日志,写慢日志
     */ 
    /* Log the command into the Slow log if needed, and populate the
     * per-command statistics that we show in INFO commandstats. */
    if (flags & CMD_CALL_SLOWLOG && c->cmd->proc != execCommand) {
        char *latency_event = (c->cmd->flags & CMD_FAST) ?
                              "fast-command" : "command";
        latencyAddSampleIfNeeded(latency_event,duration/1000);
        slowlogPushEntryIfNeeded(c,c->argv,c->argc,duration);
    }

    //...................
}

slowlog.c/slowlogPushEntryIfNeeded 
 

/**
 * 将新条目推入慢速日志。
 * 此功能将确保根据配置的最大长度修剪慢速日志。
 */ 
void slowlogPushEntryIfNeeded(client *c, 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(c,argv,argc,duration));

    //慢查询日志列表的长度 超出了配置值, 裁剪列表,删除老的的日志
    /* Remove old entries if needed. */
    while (listLength(server.slowlog) > server.slowlog_max_len)
        listDelNode(server.slowlog,listLast(server.slowlog));
}

 



 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值