LFU: add hotkeys option to redis-cli

This commit is contained in:
zhaozhao.zz 2017-10-19 14:04:39 +08:00 committed by antirez
parent 583c314725
commit 9f131c9a89

View File

@ -107,6 +107,7 @@ static struct config {
char *pattern; char *pattern;
char *rdb_filename; char *rdb_filename;
int bigkeys; int bigkeys;
int hotkeys;
int stdinarg; /* get last arg from stdin. (-x option) */ int stdinarg; /* get last arg from stdin. (-x option) */
char *auth; char *auth;
int output; /* output mode, see OUTPUT_* defines */ int output; /* output mode, see OUTPUT_* defines */
@ -1129,6 +1130,8 @@ static int parseOptions(int argc, char **argv) {
config.pipe_timeout = atoi(argv[++i]); config.pipe_timeout = atoi(argv[++i]);
} else if (!strcmp(argv[i],"--bigkeys")) { } else if (!strcmp(argv[i],"--bigkeys")) {
config.bigkeys = 1; config.bigkeys = 1;
} else if (!strcmp(argv[i],"--hotkeys")) {
config.hotkeys = 1;
} else if (!strcmp(argv[i],"--eval") && !lastarg) { } else if (!strcmp(argv[i],"--eval") && !lastarg) {
config.eval = argv[++i]; config.eval = argv[++i];
} else if (!strcmp(argv[i],"--ldb")) { } else if (!strcmp(argv[i],"--ldb")) {
@ -1229,6 +1232,8 @@ static void usage(void) {
" no reply is received within <n> seconds.\n" " no reply is received within <n> seconds.\n"
" Default timeout: %d. Use 0 to wait forever.\n" " Default timeout: %d. Use 0 to wait forever.\n"
" --bigkeys Sample Redis keys looking for big keys.\n" " --bigkeys Sample Redis keys looking for big keys.\n"
" --hotkeys Sample Redis keys looking for hot keys.\n"
" only works when maxmemory-policy is *lfu.\n"
" --scan List all keys using the SCAN command.\n" " --scan List all keys using the SCAN command.\n"
" --pattern <pat> Useful with --scan to specify a SCAN pattern.\n" " --pattern <pat> Useful with --scan to specify a SCAN pattern.\n"
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n" " --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n"
@ -2343,6 +2348,129 @@ static void findBigKeys(void) {
exit(0); exit(0);
} }
static void getKeyFreqs(redisReply *keys, unsigned long long *freqs) {
redisReply *reply;
unsigned int i;
/* Pipeline OBJECT freq commands */
for(i=0;i<keys->elements;i++) {
redisAppendCommand(context, "OBJECT freq %s", keys->element[i]->str);
}
/* Retrieve freqs */
for(i=0;i<keys->elements;i++) {
if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {
fprintf(stderr, "Error getting freq for key '%s' (%d: %s)\n",
keys->element[i]->str, context->err, context->errstr);
exit(1);
} else if(reply->type != REDIS_REPLY_INTEGER) {
if(reply->type == REDIS_REPLY_ERROR) {
fprintf(stderr, "Error: %s\n", reply->str);
exit(1);
} else {
fprintf(stderr, "Warning: OBJECT freq on '%s' failed (may have been deleted)\n", keys->element[i]->str);
freqs[i] = 0;
}
} else {
freqs[i] = reply->integer;
}
freeReplyObject(reply);
}
}
#define HOTKEYS_SAMPLE 16
static void findHotKeys(void) {
redisReply *keys, *reply;
unsigned long long counters[HOTKEYS_SAMPLE] = {0};
sds hotkeys[HOTKEYS_SAMPLE] = {NULL};
unsigned long long sampled = 0, total_keys, *freqs = NULL, it = 0;
unsigned int arrsize = 0, i, k;
double pct;
/* Total keys pre scanning */
total_keys = getDbSize();
/* Status message */
printf("\n# Scanning the entire keyspace to find hot keys as well as\n");
printf("# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec\n");
printf("# per 100 SCAN commands (not usually needed).\n\n");
/* SCAN loop */
do {
/* Calculate approximate percentage completion */
pct = 100 * (double)sampled/total_keys;
/* Grab some keys and point to the keys array */
reply = sendScan(&it);
keys = reply->element[1];
/* Reallocate our freqs array if we need to */
if(keys->elements > arrsize) {
freqs = zrealloc(freqs, sizeof(unsigned long long)*keys->elements);
if(!freqs) {
fprintf(stderr, "Failed to allocate storage for keys!\n");
exit(1);
}
arrsize = keys->elements;
}
getKeyFreqs(keys, freqs);
/* Now update our stats */
for(i=0;i<keys->elements;i++) {
sampled++;
/* Update overall progress */
if(sampled % 1000000 == 0) {
printf("[%05.2f%%] Sampled %llu keys so far\n", pct, sampled);
}
/* Use eviction pool here */
k = 0;
while (k < HOTKEYS_SAMPLE && freqs[i] > counters[k]) k++;
if (k == 0) continue;
k--;
if (k == 0 || counters[k] == 0) {
sdsfree(hotkeys[k]);
} else {
sdsfree(hotkeys[0]);
memmove(counters,counters+1,sizeof(counters[0])*k);
memmove(hotkeys,hotkeys+1,sizeof(hotkeys[0])*k);
}
counters[k] = freqs[i];
hotkeys[k] = sdsnew(keys->element[i]->str);
printf(
"[%05.2f%%] Hot key '%s' found so far with counter %llu\n",
pct, keys->element[i]->str, freqs[i]);
}
/* Sleep if we've been directed to do so */
if(sampled && (sampled %100) == 0 && config.interval) {
usleep(config.interval);
}
freeReplyObject(reply);
} while(it != 0);
if (freqs) zfree(freqs);
/* We're done */
printf("\n-------- summary -------\n\n");
printf("Sampled %llu keys in the keyspace!\n", sampled);
for (i=1; i<= HOTKEYS_SAMPLE; i++) {
k = HOTKEYS_SAMPLE - i;
if(counters[k]>0) {
printf("hot key found with counter: %llu\tkeyname: %s\n", counters[k], hotkeys[k]);
sdsfree(hotkeys[k]);
}
}
exit(0);
}
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* Stats mode * Stats mode
*--------------------------------------------------------------------------- */ *--------------------------------------------------------------------------- */
@ -2720,6 +2848,7 @@ int main(int argc, char **argv) {
config.pipe_mode = 0; config.pipe_mode = 0;
config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT; config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT;
config.bigkeys = 0; config.bigkeys = 0;
config.hotkeys = 0;
config.stdinarg = 0; config.stdinarg = 0;
config.auth = NULL; config.auth = NULL;
config.eval = NULL; config.eval = NULL;
@ -2780,6 +2909,12 @@ int main(int argc, char **argv) {
findBigKeys(); findBigKeys();
} }
/* Find hot keys */
if (config.hotkeys) {
if (cliConnect(0) == REDIS_ERR) exit(1);
findHotKeys();
}
/* Stat mode */ /* Stat mode */
if (config.stat_mode) { if (config.stat_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1); if (cliConnect(0) == REDIS_ERR) exit(1);