Commit 8c84c962 authored by antirez's avatar antirez
Browse files

MEMORY OVERHEAD implemented (using Oran Agra initial implementation).

This code was extracted from @oranagra PR #3223 and modified in order
to provide only certain amounts of information compared to the original
code. It was also moved from DEBUG to the newly introduced MEMORY
command. Thanks to Oran for the implementation and the PR.

It implements detailed memory usage stats that can be useful in both
provisioning and troubleshooting memory usage in Redis.
parent 89dec692
...@@ -867,7 +867,107 @@ void memoryCommand(client *c) { ...@@ -867,7 +867,107 @@ void memoryCommand(client *c) {
usage += sdsAllocSize(c->argv[1]->ptr); usage += sdsAllocSize(c->argv[1]->ptr);
usage += sizeof(dictEntry); usage += sizeof(dictEntry);
addReplyLongLong(c,usage); addReplyLongLong(c,usage);
} else if (!strcasecmp(c->argv[1]->ptr,"overhead") && c->argc == 2) {
int j;
size_t mem_total = 0;
size_t mem = 0;
size_t zmalloc_used = zmalloc_used_memory();
int toplevel_keys = 8;
void *tlk = addDeferredMultiBulkLength(c);
addReplyBulkCString(c,"total.allocated");
addReplyLongLong(c,zmalloc_used);
addReplyBulkCString(c,"startup.allocated");
addReplyLongLong(c,server.initial_memory_usage);
mem_total += server.initial_memory_usage;
mem = 0;
if (server.repl_backlog)
mem += zmalloc_size(server.repl_backlog);
addReplyBulkCString(c,"replication.backlog");
addReplyLongLong(c,mem);
mem_total += mem;
mem = 0;
if (listLength(server.slaves)) {
listIter li;
listNode *ln;
listRewind(server.slaves,&li);
while((ln = listNext(&li))) {
client *client = listNodeValue(ln);
mem += getClientOutputBufferMemoryUsage(client);
mem += sdsAllocSize(client->querybuf);
mem += sizeof(client);
}
}
addReplyBulkCString(c,"clients.slaves");
addReplyLongLong(c,mem);
mem_total+=mem;
mem = 0;
if (listLength(server.clients)) {
listIter li;
listNode *ln;
listRewind(server.clients,&li);
while((ln = listNext(&li))) {
client *client = listNodeValue(ln);
if (client->flags & CLIENT_SLAVE)
continue;
mem += getClientOutputBufferMemoryUsage(client);
mem += sdsAllocSize(client->querybuf);
mem += sizeof(client);
}
}
addReplyBulkCString(c,"clients.normal");
addReplyLongLong(c,mem);
mem_total+=mem;
mem = 0;
if (server.aof_state != AOF_OFF) {
mem += sdslen(server.aof_buf);
mem += aofRewriteBufferSize();
}
addReplyBulkCString(c,"aof.buffer");
addReplyLongLong(c,mem);
mem_total+=mem;
for (j = 0; j < server.dbnum; j++) {
redisDb *db = server.db+j;
long long keyscount = dictSize(db->dict);
if (keyscount==0) continue;
char dbname[32];
toplevel_keys++;
snprintf(dbname,sizeof(dbname),"db.%d",j);
addReplyBulkCString(c,dbname);
addReplyMultiBulkLen(c,4);
mem = dictSize(db->dict) * sizeof(dictEntry) +
dictSlots(db->dict) * sizeof(dictEntry*) +
dictSize(db->dict) * sizeof(robj);
addReplyBulkCString(c,"overhead.hashtable.main");
addReplyLongLong(c,mem);
mem_total+=mem;
mem = dictSize(db->expires) * sizeof(dictEntry) +
dictSlots(db->expires) * sizeof(dictEntry*);
addReplyBulkCString(c,"overhead.hashtable.expires");
addReplyLongLong(c,mem);
mem_total+=mem;
}
addReplyBulkCString(c,"overhead.total");
addReplyLongLong(c,mem_total);
addReplyBulkCString(c,"dataset");
addReplyLongLong(c,zmalloc_used - mem_total);
setDeferredMultiBulkLength(c,tlk,toplevel_keys*2);
} else { } else {
addReplyError(c,"Syntax error. Try MEMORY usage <key>"); addReplyError(c,"Syntax error. Try MEMORY [usage <key>] | [overhead]");
} }
} }
...@@ -274,7 +274,7 @@ struct redisCommand redisCommandTable[] = { ...@@ -274,7 +274,7 @@ struct redisCommand redisCommandTable[] = {
{"readwrite",readwriteCommand,1,"F",0,NULL,0,0,0,0,0}, {"readwrite",readwriteCommand,1,"F",0,NULL,0,0,0,0,0},
{"dump",dumpCommand,2,"r",0,NULL,1,1,1,0,0}, {"dump",dumpCommand,2,"r",0,NULL,1,1,1,0,0},
{"object",objectCommand,3,"r",0,NULL,2,2,2,0,0}, {"object",objectCommand,3,"r",0,NULL,2,2,2,0,0},
{"memory",memoryCommand,3,"r",0,NULL,0,0,0,0,0}, {"memory",memoryCommand,-2,"r",0,NULL,0,0,0,0,0},
{"client",clientCommand,-2,"as",0,NULL,0,0,0,0,0}, {"client",clientCommand,-2,"as",0,NULL,0,0,0,0,0},
{"eval",evalCommand,-3,"s",0,evalGetKeys,0,0,0,0,0}, {"eval",evalCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},
{"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,0,0,0,0,0}, {"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},
...@@ -1860,6 +1860,7 @@ void initServer(void) { ...@@ -1860,6 +1860,7 @@ void initServer(void) {
slowlogInit(); slowlogInit();
latencyMonitorInit(); latencyMonitorInit();
bioInit(); bioInit();
server.initial_memory_usage = zmalloc_used_memory();
} }
/* Populates the Redis Command Table starting from the hard coded list /* Populates the Redis Command Table starting from the hard coded list
......
...@@ -801,6 +801,7 @@ struct redisServer { ...@@ -801,6 +801,7 @@ struct redisServer {
int cronloops; /* Number of times the cron function run */ int cronloops; /* Number of times the cron function run */
char runid[CONFIG_RUN_ID_SIZE+1]; /* ID always different at every exec. */ char runid[CONFIG_RUN_ID_SIZE+1]; /* ID always different at every exec. */
int sentinel_mode; /* True if this instance is a Sentinel. */ int sentinel_mode; /* True if this instance is a Sentinel. */
size_t initial_memory_usage; /* Bytes used after initialization. */
/* Modules */ /* Modules */
dict *moduleapi; /* Exported APIs dictionary for modules. */ dict *moduleapi; /* Exported APIs dictionary for modules. */
list *loadmodule_queue; /* List of modules to load at startup. */ list *loadmodule_queue; /* List of modules to load at startup. */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment