23.0 慢查询日志
主要的功能:记录执行时间超过给定时长的命令请求,可以用这个功能产生的日志来监视和优化查询速度
- 两个选项:时间和最多保存的条目
- slowlog-log-slower-than 执行时间超过多少微秒的命令记录到日志
- slowlog-max-len指定服务器最多保存多少条查询日志(先进先出,如果日志条目满了,最早的一条会被删除)
- example one
127.0.0.1:6379> config set slowlog-max-len 5
OK
127.0.0.1:6379> config set slowlog-log-slower-than 0
OK
127.0.0.1:6379> set name "wang wang"
OK
127.0.0.1:6379> set sex "girl"
OK
127.0.0.1:6379> set age "30"
OK
127.0.0.1:6379> set year "2020"
OK
127.0.0.1:6379> set family "four"
OK
127.0.0.1:6379> slowlog get
1) 1) (integer) 10
2) (integer) 1603691289
3) (integer) 9
4) 1) "set"
2) "family"
3) "four"
5) "127.0.0.1:54133"
6) ""
2) 1) (integer) 9
2) (integer) 1603691281
3) (integer) 30
4) 1) "set"
2) "year"
3) "2020"
5) "127.0.0.1:54133"
6) ""
3) 1) (integer) 8
2) (integer) 1603691267
3) (integer) 34
4) 1) "set"
2) "age"
3) "30"
5) "127.0.0.1:54133"
6) ""
4) 1) (integer) 7
2) (integer) 1603691243
3) (integer) 25
4) 1) "set"
2) "sex"
3) "girl"
5) "127.0.0.1:54133"
6) ""
5) 1) (integer) 6
2) (integer) 1603691234
3) (integer) 959
4) 1) "set"
2) "name"
3) "wang wang"
5) "127.0.0.1:54133"
6) ""
- example two
- 再输入slowlog get,能看到上一次的slowlog get已经被记录到了慢查询日志中,最旧的那条日志已经被删除了
127.0.0.1:6379> slowlog get
1) 1) (integer) 11
2) (integer) 1603691298
3) (integer) 66
4) 1) "slowlog"
2) "get"
5) "127.0.0.1:54133"
6) ""
2) 1) (integer) 10
2) (integer) 1603691289
3) (integer) 9
4) 1) "set"
2) "family"
3) "four"
5) "127.0.0.1:54133"
6) ""
3) 1) (integer) 9
2) (integer) 1603691281
3) (integer) 30
4) 1) "set"
2) "year"
3) "2020"
5) "127.0.0.1:54133"
6) ""
4) 1) (integer) 8
2) (integer) 1603691267
3) (integer) 34
4) 1) "set"
2) "age"
3) "30"
5) "127.0.0.1:54133"
6) ""
5) 1) (integer) 7
2) (integer) 1603691243
3) (integer) 25
4) 1) "set"
2) "sex"
3) "girl"
5) "127.0.0.1:54133"
6) ""
23.1 慢查询记录的保存
- 服务器状态中包含了几个和慢查询日志功能有关的属性
//code0 server.h
struct redisServer {
/* General */
pid_t pid; /* Main process pid. */
char *configfile; /* Absolute config file path, or NULL */
char *executable; /* Absolute executable file path. */
// ...
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 */
//...
}
- slowlog_entry_id的初始值位0,每次新创建一条新的慢查询日志时,这个属性的值会被用作新日志的id值,之后程序会对这个属性的值增一。
- slowlog链表表示了服务器中的所有的慢查询日志,链表的每一个节点保存了slowlogEntry结构,最新的在表头,新的日志总是添加到表头
//code1 slowlog.h
/* 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. */
//命令执行时的时间(start time)
time_t time; /* Unix time at which the query was executed. */
sds cname; /* Client name. */
sds peerid; /* Client network address. */
} slowlogEntry;
23.2 慢查询日志的阅览和删除
- SLOWLOG GET命令:获取日志
- SLOWLOG LEN命令:获取日志长度
- SLOGLOG RESET命令:清空日志
//code2 slowlog.c
/* 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"))
{
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 = addReplyDeferredLen(c);
while(count-- && (ln = listNext(&li))) {
int j;
se = ln->value;
addReplyArrayLen(c,6);
addReplyLongLong(c,se->id);
addReplyLongLong(c,se->time);
addReplyLongLong(c,se->duration);
addReplyArrayLen(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++;
}
setDeferredArrayLen(c,totentries,sent);
} else {
addReplySubcommandSyntaxError(c);
}
}
23.2 添加新日志
- 执行命令之前和之后,会记录微秒格式的UNIX时间戳,时间差就是服务器执行命令耗费的时间
- 服务器会将这个时间差传给slowlogPushEntryIfNeeded,负责检查是否需要为这次命令创建慢查询日志
- 如果需要新创建一条慢查询日志,调用slowlogCreateEntry函数(创建一条新的慢查询日志,且将server.slowlog_entry_id++)
/* 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(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));
}