美文网首页
Redis慢查询日志

Redis慢查询日志

作者: 一只木鸟 | 来源:发表于2018-12-01 20:09 被阅读0次

    版本:redis-4.0.9

    数据结构

    struct redisServer {/*slow log相关变量*/
        ...
        list *slowlog; /* SLOWLOG list of commands */
        long long slowlog_entry_id; /* SLOWLOG current entry ID */
        long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */
        unsigned long slowlog_max_len; /* SLOWLOG max number of items logged */
        ...
    };
    
    typedef struct list {/*Redis慢日志以list方式存储*/
        listNode *head;
        listNode *tail;
        void *(*dup)(void *ptr);
        void (*free)(void *ptr);
        int (*match)(void *ptr, void *key);
        unsigned long len;
    } list;
    
    typedef struct listNode {/*list元素*/
        struct listNode *prev;
        struct listNode *next;
        void *value;
    } listNode;
    
    /* 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;
    
    typedef struct client {/*客户端结构体,列出两个相关数据项*/
        ...
        robj **argv; /* Arguments of current command. 存储当前命令和参数。*/
        int argc; /* Num of arguments of current command.记录argv数组的个数,表示当前命令的参数个数 。*/
        ...
    } client;
    

    处理过程

    call() -> slowlogPushEntryIfNeeded()->slowlogCreateEntry()
    

    相关功能函数

    void call(client *c, int flags) {/*慢查询函数入口*/
        ...
        /* Log the command into the Slow log if needed*/
        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);
        }
        ...
    }
    
    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));
    }
    
    
    slowlogEntry *slowlogCreateEntry(client *c, 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++) {
            ...
        }
        se->time = time(NULL);
        se->duration = duration;
        se->id = server.slowlog_entry_id++;
        se->peerid = sdsnew(getClientPeerId(c));
        se->cname = c->name ? sdsnew(c->name->ptr) : sdsempty();
        return se;
    }
    
    
    void slowlogCommand(client *c) {/*slowlog命令函数,其中get选项显示慢日志信息*/
        if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) {
            ...
        } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) {
            ...
        } 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,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);
    }
    

    可以看到:

    • 慢日志以list存储。
    • slowlog-log-slower-than<0 表示禁用慢日志。
    • listLength(server.slowlog) > server.slowlog_max_len 会删除掉list最后一个node。

    慢日志结构:

    1) 1) (integer) 117            //编号,对应slowlog_entry_id变量
        2) (integer) 1543662242    //慢查询时间戳,对应slowlogEntry中的time数据项
        3) (integer) 275           //慢查询执行时间,对应duration,单位微妙
        4)  1) "MSET”              //慢查询命令和参数
            2) "key:000000096553"
            3) "xxx"
            4) "key:000000084369"
            5) "xxx"
            6) "key:000000021897"
            7) "xxx"
            8) "key:000000098780"
            9) "xxx"
           10) "key:000000009550"
           11) "xxx"
           12) "key:000000041990"
           13) "xxx"
           14) "key:000000053892"
           15) "xxx"
           16) "key:000000008844"
           17) "xxx"
           18) "key:000000098473"
           19) "xxx"
           20) "key:000000011879"
           21) "xxx"
        5) "192.168.18.1:54182”    //客户端ip,port,对应peerid
        6) “”                      //客户端名,对应cname
    

    Python程序显示慢查询

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    __author__ = 'gangziliu'
    __date__ = '2018/12/3 15:00:00'
    
    
    import os
    import sys
    import getopt
    import argparse
    import redis
    import json
    import time
    import math
    
    #用法:
    #1、单个Redis实例:
    #    python redis_slowlog.py --host 192.168.18.1 -p 6379 -bt '2018-12-03 15:00:00' -et '2018-12-03 16:00:00'
    #    格式化显示192.168.18.1:6379 redis慢日志中时间大于2018-12-03 15:00:00 并且小于2018-12-03 16:00:00的记录
    #2、多个Redis实例:
    #    python redis_slowlog.py -f list.txt -bt '2018-12-03 15:00:00' -et '2018-12-03 16:00:00'
    #    格式化显示list.txt中的Redis实例慢日志中时间大于2018-12-03 15:00:00并且小于2018-12-03 16:00:00的记录
    #    其中list.txt格式如下:
    #        192.168.18.1:6379
    #        192.168.18.1:6380
    #        192.168.18.2:7000
    
    
    def Util(time):
        str = ('微妙','毫秒','秒')
        digit = math.trunc(math.log10(time)/math.log10(1000))
        s_time =int(time/math.pow(1000,digit))
        s_unit = str[digit]
        return s_time, s_unit
    
    def slowlogDeplayOneRedis(hostname, port, begtime, endtime):
        conn = redis.Redis(host=hostname, port=port)
        len = conn.slowlog_len()
        slowlog = conn.slowlog_get(len)
        for i in range(len):
            s_id = slowlog[i]['id']
            s_datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(slowlog[i]['start_time']))
            s_time = slowlog[i]['duration']
            s_time, s_unit = Util(s_time)
            s_command = slowlog[i]['command']
            if s_datetime > begtime and s_datetime < endtime:
                print("编号: %s\t开始时间: %s\t耗时: %s %s\t命令: %s" % (s_id, s_datetime, s_time, s_unit, s_command))
    
    def slowlogDeplaySomeRedis(filename,begtime,endtime):
        with open(filename,'rt') as f:
            for line in f:
                line=line.strip('\n')
                print('##########################%s#######################' % line)
                redis_instance = line.split(':')
                slowlogDeplayOneRedis(redis_instance[0], redis_instance[1], begtime, endtime)
        f.close()
    
    def slowlogDeplay(hostname, port, filename, begtime, endtime):
        if hostname is None and port is None:
           slowlogDeplaySomeRedis(filename,begtime,endtime)
        elif filename is None:
           slowlogDeplayOneRedis(hostname,port,begtime,endtime)
    
    
    def main(argv):
        parser = argparse.ArgumentParser()
        parser.add_argument('--host', action='store', dest='hostname', help='hostname of redis server')
        parser.add_argument('-p','--port', action='store', dest='port', help='port of redis server')
        parser.add_argument('-f','--file', action='store', dest='filename', help='message list file of redis server')
        parser.add_argument('-bt','--begintime', action='store', dest='begtime', help='show slowlog that the datetime more than datetime')
        parser.add_argument('-et','--endtime', action='store', dest='endtime', help='show slowlog that the datetime less than datetime')
        results = parser.parse_args()
        slowlogDeplay(results.hostname,results.port,results.filename,results.begtime,results.endtime)
     
    if __name__ == "__main__":
        main(sys.argv[1:])
    
    
    image.png

    相关文章

      网友评论

          本文标题:Redis慢查询日志

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