由于诸多原因,现在已经很少能长篇大论介绍Redis的相关技术,但日常工作以及脑子中经常想整理和总结一些Redis的点点滴滴,既能帮助自己记录问题,又能帮助他人减少碰到类似问题,于是有个想法,准备写一个小的系列:Redis小功能大用处。
本文将介绍Redis 3后一个新的统计项total_net_output_bytes。
一、问题
之前有朋友问我,Redis统计的网络输出流量总值total_net_output_bytes和瞬时值instantaneous_output_kbps比实际机器统计的要高。(Redis版本3.2.3)
经试验证明,确实如此:一个机器的输出网络流量远远小于几个Redis实例的。
1.单机Redis实例网络输出流量统计:
2.单机整机网络输出流量统计:
一般碰到这类问题,习惯性的在源码里找下,看看到底是如何计算的。
二、网络输出流量计算
代码里搜server.stat_net_output_bytes,找到如下这个代码
1. networking.c的writeToClient函数:
将输出缓冲区数据通过socket发送给客户端,源码比较长:
/* Write data in output buffers to client
int writeToClient(int fd, client *c, int handler_installed) {
ssize_t nwritten = 0, totwritten = 0;
size_t objlen;
size_t objmem;
robj *o;
while(clientHasPendingReplies(c)) {
if (c->bufpos > 0) {
nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
if (nwritten <= 0) break;
c->sentlen += nwritten;
totwritten += nwritten;
if ((int)c->sentlen == c->bufpos) {
c->bufpos = 0;
c->sentlen = 0;
}
} else {
o = listNodeValue(listFirst(c->reply));
objlen = sdslen(o->ptr);
objmem = getStringObjectSdsUsedMemory(o);
if (objlen == 0) {
listDelNode(c->reply,listFirst(c->reply));
c->reply_bytes -= objmem;
continue;
}
nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);
if (nwritten <= 0) break;
c->sentlen += nwritten;
totwritten += nwritten;
if (c->sentlen == objlen) {
listDelNode(c->reply,listFirst(c->reply));
c->sentlen = 0;
c->reply_bytes -= objmem;
}
}
server.stat_net_output_bytes += totwritten;
if (totwritten > NET_MAX_WRITES_PER_EVENT &&
(server.maxmemory == 0 ||
zmalloc_used_memory() break;
}
.............忽略忽略忽略..............
2. 基础知识:
Redis为每个客户端配置了输入缓冲区和输出缓冲区,用户暂存发送的命令和返回的结果,其中输出缓冲区分为:普通客户端缓冲区、复制客户端缓冲区、pubsub客户端缓冲区,并且输出缓冲区内部分为bufpos用于缓存一些小结果集,另外设置一个队列缓存大结果:
(1) 小结果集:
typedef struct redisClient {
// 缓存小结果集
char buf[REDIS_REPLY_CHUNK_BYTES]
int bufpos
}
(2) 小结果集:
typedef struct redisClient {
// 缓存队列
list *reply
}
三、Redis作者的低级失误:
server.stat_net_output_bytes记录从输出缓冲区累加的字节数,但3.2.3之前似乎放错了位置,导致过大。
可以想到这个bug肯定已经被提过并修复了(https://github.com/redis/redis/commit/d70ac1d1)
四、其他
1.instantaneous_output_kbps:total_net_output_bytes是一个累计值,为了方便看到瞬时值提供了instantaneous_output_kbps,它在内部用采样的方法计算的,如果你比较懒,不乐意自己按一定时间计算差值,可以使用instantaneous_output_kbps。
instantaneous_output_kbps = (float)getInstantaneousMetric(STATS_METRIC_NET_OUTPUT)/1024,
/* Return the mean of all the samples. */
long long getInstantaneousMetric(int metric) {
int j;
long long sum = 0;
for (j = 0; j sum += server.inst_metric[metric].samples[j];
return sum / STATS_METRIC_SAMPLES;
}
2.作用
该统计指标非常重要:通过对其监控,规划和防止打爆机器网卡(千兆、万兆)、找到bigkey(例如执行大的mget、hgetall、lrange等等)。
五、最后
希望自己有足够的时间学习和撰写文章,希望2021会更好。