慢查询、pipeline流水线和数据库
本文将对Redis一些 相对冷门的知识点进行介绍,分别是慢查询、pipeline管道和数据库。
说明:本文引用Redis 源码版本为: 6.0.19
慢查询
在关系型数据库比如说Mysql中,慢查询日志是一种性能优化手段,通过开启慢查询日志,然后针对运行速度较慢的sql语句进行相应优化。实际上在Redis中也有类似的概念。
在Redis中,一条命令的生命周期大致是:客户端发送命令、命令排队、执行命令和回复结果。慢查询就发生在执行命令这个阶段。
具体执行命令的逻辑在server.c中的void call(client *c, int flags)函数中,以下是统计命令运行时间的代码
start = server.ustime; // 记录开始时间
c->cmd->proc(c); // 执行命令
duration = ustime()-start; // 统计运行命令占用的时间
而后进行相关判断
/* Log the command into the Slow log if needed, and populate the
* per-command statistics that we show in INFO commandstats. */
// 调用call的时候设置了慢查询且命令没有设置忽略慢查询
if (flags & CMD_CALL_SLOWLOG && !(c->cmd->flags & CMD_SKIP_SLOWLOG)) {
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中。
/* 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));
}
本质上,slowlog是一个双向链表,加入的时候默认加载头部,默认去除链表的最后一个节点。
pipeline管道
pipeline管道是Redis提供的批量执行命令的功能。Redis的命令执行速度相对较快,可能比命令发送回复的时间还要短,这种情况下,Redis的性能瓶颈就在这个网络上了。而在管道机制下,客户端一次性发送一批命令给Redis执行,当然在服务端这些命令还是顺序执行,然后再一起打包返回。
管道技术也存在一些缺点,比如说不能保证原子性,并且如果命令过多,会占用大量内存。
数据库
我们都知道,在连接关系型数据库比如Mysql中,不但要指定数据库服务器服务的ip地址和端口号,可能还需要指定使用那个数据库database,实际上在Redis中也有类似的概念,只不过很多时候都是默认使用数据库db0。
在初始化redisserver中,会默认创建16个数据库,代码如下。16是由配置文件的databases字段决定的。
/* Create the Redis databases, and initialize other internal state. */
for (j = 0; j < server.dbnum; j++) {
server.db[j].dict = dictCreate(&dbDictType,NULL);
server.db[j].expires = dictCreate(&keyptrDictType,NULL);
server.db[j].expires_cursor = 0;
server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);
server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
server.db[j].id = j;
server.db[j].avg_ttl = 0;
server.db[j].defrag_later = listCreate();
listSetFreeMethod(server.db[j].defrag_later,(void (*)(void*))sdsfree);
}
默认使用db0,但是可以通过select命令切换选中的数据库,命令实现逻辑如下
void selectCommand(client *c) {
long id;
if (getLongFromObjectOrReply(c, c->argv[1], &id,
"invalid DB index") != C_OK) // 提取数据库编号且完成校应
return;
if (server.cluster_enabled && id != 0) { // 集群模式下只能使用db0
addReplyError(c,"SELECT is not allowed in cluster mode");
return;
}
if (selectDb(c,id) == C_ERR) {
addReplyError(c,"DB index is out of range");
} else {
addReply(c,shared.ok);
}
}
多数据库提高了管理数据的灵活性和可靠性,可以将不同类型的数据保存到不同数据库中。
173万+

被折叠的 条评论
为什么被折叠?



