diff --git a/src/redis.c b/src/redis.c index 6375555b..99ff1898 100644 --- a/src/redis.c +++ b/src/redis.c @@ -621,6 +621,9 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { if (server.appendonly) rewriteAppendOnlyFileBackground(); } } + + /* Update fragmentation info, used for fast RSS estimation */ + if (!(loops % 10)) server.fragmentation = zmalloc_get_fragmentation_ratio(); return 100; } @@ -735,6 +738,7 @@ void initServerConfig() { server.requirepass = NULL; server.rdbcompression = 1; server.activerehashing = 1; + server.fragmentation = 1; server.maxclients = 0; server.blpop_blocked_clients = 0; server.maxmemory = 0; @@ -1075,6 +1079,8 @@ sds genRedisInfoString(void) { "blocked_clients:%d\r\n" "used_memory:%zu\r\n" "used_memory_human:%s\r\n" + "used_memory_rss:%zu\r\n" + "used_memory_estimated_rss:%zu\r\n" "mem_fragmentation_ratio:%.2f\r\n" "use_tcmalloc:%d\r\n" "changes_since_last_save:%lld\r\n" @@ -1110,6 +1116,8 @@ sds genRedisInfoString(void) { server.blpop_blocked_clients, zmalloc_used_memory(), hmem, + zmalloc_get_rss(), + redisEstimateRSS(), zmalloc_get_fragmentation_ratio(), #ifdef USE_TCMALLOC 1, @@ -1229,6 +1237,40 @@ int tryFreeOneObjectFromFreelist(void) { } } +/* A fast RSS sampling function. + * + * The function is reasonably accurate while fast, since it uses the trick of + * using the server.fragmentation ratio that is computed every second and + * is the ratio between the RSS and our zmalloc() count of allocated bytes. + * + * So in order to compute the current RSS used we just need to multiply + * the zmalloc() memory reporting, that is as fast as reading a counter, + * for the latest estimation of fragmentation. + * + * The behavior of this function is also very desirable because it is + * very responsive to memory changes: while the real RSS is actually measured + * in pages, the RSS estimation will actually change even if just a few bytes + * are freed, and this is a good property when the function is used in order + * to evict keys for Virtual Memory of for 'maxmemory' directive. + * + * Note that when the memory reported by zmalloc is smaller than the RSS + * (that is, fragmentation < 1) this means that something is odd (many pages + * swapped since the Redis instance is idle) and we consider the fragmentation + * ratio 1. */ +size_t redisEstimateRSS(void) { + size_t used = zmalloc_used_memory(); + float maxfrag; + + if (server.fragmentation < 1) return used; + maxfrag = (float)SIZE_MAX / used; + + /* If multiplying memory usage reported by zmalloc per fragmentation + * ratio will lead to an overflow we just return SIZE_MAX. */ + if (maxfrag < server.fragmentation) return SIZE_MAX; + + return (size_t)((used * server.fragmentation)); +} + /* This function gets called when 'maxmemory' is set on the config file to limit * the max memory used by the server, and we are out of memory. * This function will try to, in order: diff --git a/src/redis.h b/src/redis.h index cf21447d..11b44606 100644 --- a/src/redis.h +++ b/src/redis.h @@ -397,6 +397,8 @@ struct redisServer { char *requirepass; int rdbcompression; int activerehashing; + /* Memory related */ + float fragmentation; /* Replication related */ int isslave; char *masterauth; @@ -751,6 +753,7 @@ void usage(); void updateDictResizePolicy(void); int htNeedsResize(dict *dict); void oom(const char *msg); +size_t redisEstimateRSS(void); /* Virtual Memory */ void vmInit(void);