版本: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
网友评论