Redis慢查询日志

版本: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慢查询日志)