summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2016-07-20 15:00:35 +0200
committerantirez <antirez@gmail.com>2016-07-20 15:00:35 +0200
commit6854c7b9ee16595f74a773b911b6a195384e9665 (patch)
treef16b6b34fb27e45c3891fe9e22b8252666402375
parent6416ab19d0bd51fa90c17d856baebd581a7d991c (diff)
downloadredis-lfu.tar.gz
LFU: make counter log factor and decay time configurable.lfu
-rw-r--r--redis.conf82
-rw-r--r--src/config.c16
-rw-r--r--src/evict.c5
-rw-r--r--src/server.c2
-rw-r--r--src/server.h4
5 files changed, 95 insertions, 14 deletions
diff --git a/redis.conf b/redis.conf
index 67cd50245..05158b4e7 100644
--- a/redis.conf
+++ b/redis.conf
@@ -492,7 +492,7 @@ slave-priority 100
############################## MEMORY MANAGEMENT ################################
-# Don't use more memory than the specified amount of bytes.
+# Set a memory usage limit to the specified amount of bytes.
# When the memory limit is reached Redis will try to remove keys
# according to the eviction policy selected (see maxmemory-policy).
#
@@ -501,8 +501,8 @@ slave-priority 100
# that would use more memory, like SET, LPUSH, and so on, and will continue
# to reply to read-only commands like GET.
#
-# This option is usually useful when using Redis as an LRU cache, or to set
-# a hard memory limit for an instance (using the 'noeviction' policy).
+# This option is usually useful when using Redis as an LRU or LFU cache, or to
+# set a hard memory limit for an instance (using the 'noeviction' policy).
#
# WARNING: If you have slaves attached to an instance with maxmemory on,
# the size of the output buffers needed to feed the slaves are subtracted
@@ -520,12 +520,20 @@ slave-priority 100
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
-# volatile-lru -> remove the key with an expire set using an LRU algorithm
-# allkeys-lru -> remove any key according to the LRU algorithm
-# volatile-random -> remove a random key with an expire set
-# allkeys-random -> remove a random key, any key
-# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
-# noeviction -> don't expire at all, just return an error on write operations
+# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
+# allkeys-lru -> Evict any key using approximated LRU.
+# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
+# allkeys-lfu -> Evict any key using approximated LFU.
+# volatile-random -> Remove a random key among the ones with an expire set.
+# allkeys-random -> Remove a random key, any key.
+# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
+# noeviction -> Don't evict anything, just return an error on write operations.
+#
+# LRU means Least Recently Used
+# LFU means Least Frequently Used
+#
+# Both LRU, LFU and volatile-ttl are implemented using approximated
+# randomized algorithms.
#
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
@@ -540,14 +548,14 @@ slave-priority 100
#
# maxmemory-policy noeviction
-# LRU and minimal TTL algorithms are not precise algorithms but approximated
+# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can tune it for speed or
# accuracy. For default Redis will check five keys and pick the one that was
# used less recently, you can change the sample size using the following
# configuration directive.
#
# The default of 5 produces good enough results. 10 Approximates very closely
-# true LRU but costs a bit more CPU. 3 is very fast but not very accurate.
+# true LRU but costs more CPU. 3 is faster but not very accurate.
#
# maxmemory-samples 5
@@ -1113,3 +1121,55 @@ hz 10
# in order to commit the file to the disk more incrementally and avoid
# big latency spikes.
aof-rewrite-incremental-fsync yes
+
+# Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good
+# idea to start with the default settings and only change them after investigating
+# how to improve the performances and how the keys LFU change over time, which
+# is possible to inspect via the OBJECT FREQ command.
+#
+# There are two tunable parameters in the Redis LFU implementation: the
+# counter logarithm factor and the counter decay time. It is important to
+# understand what the two parameters mean before changing them.
+#
+# The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis
+# uses a probabilistic increment with logarithmic behavior. Given the value
+# of the old counter, when a key is accessed, the counter is incremented in
+# this way:
+#
+# 1. A random number R between 0 and 1 is extracted.
+# 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1).
+# 3. The counter is incremented only if R < P.
+#
+# The default lfu-log-factor is 10. This is a table of how the frequency
+# counter changes with a different number of accesses with different
+# logarithmic factors:
+#
+# +--------+------------+------------+------------+------------+------------+
+# | factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits |
+# +--------+------------+------------+------------+------------+------------+
+# | 0 | 104 | 255 | 255 | 255 | 255 |
+# +--------+------------+------------+------------+------------+------------+
+# | 1 | 18 | 49 | 255 | 255 | 255 |
+# +--------+------------+------------+------------+------------+------------+
+# | 10 | 10 | 18 | 142 | 255 | 255 |
+# +--------+------------+------------+------------+------------+------------+
+# | 100 | 8 | 11 | 49 | 143 | 255 |
+# +--------+------------+------------+------------+------------+------------+
+#
+# NOTE: The above table was obtained by running the following commands:
+#
+# redis-benchmark -n 1000000 incr foo
+# redis-cli object freq foo
+#
+# NOTE 2: The counter initial value is 5 in order to give new objects a chance
+# to accumulate hits.
+#
+# The counter decay time is the time, in minutes, that must elapse in order
+# for the key counter to be divided by two (or decremented if it has a value
+# less <= 10).
+#
+# The default value for the lfu-decay-time is 1. A Special value of 0 means to
+# decay the counter every time it happens to be scanned.
+#
+# lfu-log-factor 10
+# lfu-decay-time 1
diff --git a/src/config.c b/src/config.c
index f443ce22a..686f80cfd 100644
--- a/src/config.c
+++ b/src/config.c
@@ -324,6 +324,18 @@ void loadServerConfigFromString(char *config) {
err = "maxmemory-samples must be 1 or greater";
goto loaderr;
}
+ } else if (!strcasecmp(argv[0],"lfu-log-factor") && argc == 2) {
+ server.lfu_log_factor = atoi(argv[1]);
+ if (server.maxmemory_samples < 0) {
+ err = "lfu-log-factor must be 0 or greater";
+ goto loaderr;
+ }
+ } else if (!strcasecmp(argv[0],"lfu-decay-time") && argc == 2) {
+ server.lfu_decay_time = atoi(argv[1]);
+ if (server.maxmemory_samples < 1) {
+ err = "lfu-decay-time must be 0 or greater";
+ goto loaderr;
+ }
} else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
slaveof_linenum = linenum;
server.masterhost = sdsnew(argv[1]);
@@ -956,6 +968,10 @@ void configSetCommand(client *c) {
} config_set_numerical_field(
"maxmemory-samples",server.maxmemory_samples,1,LLONG_MAX) {
} config_set_numerical_field(
+ "lfu-log-factor",server.lfu_log_factor,0,LLONG_MAX) {
+ } config_set_numerical_field(
+ "lfu-decay-time",server.lfu_decay_time,0,LLONG_MAX) {
+ } config_set_numerical_field(
"timeout",server.maxidletime,0,LONG_MAX) {
} config_set_numerical_field(
"auto-aof-rewrite-percentage",server.aof_rewrite_perc,0,LLONG_MAX){
diff --git a/src/evict.c b/src/evict.c
index d791415a5..4a4ba2ea9 100644
--- a/src/evict.c
+++ b/src/evict.c
@@ -287,13 +287,12 @@ unsigned long LFUTimeElapsed(unsigned long ldt) {
/* Logarithmically increment a counter. The greater is the current counter value
* the less likely is that it gets really implemented. Saturate it at 255. */
-#define LFU_LOG_FACTOR 10
uint8_t LFULogIncr(uint8_t counter) {
if (counter == 255) return 255;
double r = (double)rand()/RAND_MAX;
double baseval = counter - LFU_INIT_VAL;
if (baseval < 0) baseval = 0;
- double p = 1.0/(baseval*LFU_LOG_FACTOR+1);
+ double p = 1.0/(baseval*server.lfu_log_factor+1);
if (r < p) counter++;
return counter;
}
@@ -308,7 +307,7 @@ uint8_t LFULogIncr(uint8_t counter) {
unsigned long LFUDecrAndReturn(robj *o) {
unsigned long ldt = o->lru >> 8;
unsigned long counter = o->lru & 255;
- if (LFUTimeElapsed(ldt) >= LFU_DECR_INTERVAL && counter) {
+ if (LFUTimeElapsed(ldt) >= server.lfu_decay_time && counter) {
if (counter > LFU_INIT_VAL*2) {
counter /= 2;
if (counter < LFU_INIT_VAL*2) counter = LFU_INIT_VAL*2;
diff --git a/src/server.c b/src/server.c
index f8847f646..abb98edfd 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1341,6 +1341,8 @@ void initServerConfig(void) {
server.maxmemory = CONFIG_DEFAULT_MAXMEMORY;
server.maxmemory_policy = CONFIG_DEFAULT_MAXMEMORY_POLICY;
server.maxmemory_samples = CONFIG_DEFAULT_MAXMEMORY_SAMPLES;
+ server.lfu_log_factor = CONFIG_DEFAULT_LFU_LOG_FACTOR;
+ server.lfu_decay_time = CONFIG_DEFAULT_LFU_DECAY_TIME;
server.hash_max_ziplist_entries = OBJ_HASH_MAX_ZIPLIST_ENTRIES;
server.hash_max_ziplist_value = OBJ_HASH_MAX_ZIPLIST_VALUE;
server.list_max_ziplist_size = OBJ_LIST_MAX_ZIPLIST_SIZE;
diff --git a/src/server.h b/src/server.h
index 6a9fb2a09..266c5336d 100644
--- a/src/server.h
+++ b/src/server.h
@@ -129,6 +129,8 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
#define CONFIG_DEFAULT_MAXMEMORY 0
#define CONFIG_DEFAULT_MAXMEMORY_SAMPLES 5
+#define CONFIG_DEFAULT_LFU_LOG_FACTOR 10
+#define CONFIG_DEFAULT_LFU_DECAY_TIME 1
#define CONFIG_DEFAULT_AOF_FILENAME "appendonly.aof"
#define CONFIG_DEFAULT_AOF_NO_FSYNC_ON_REWRITE 0
#define CONFIG_DEFAULT_AOF_LOAD_TRUNCATED 1
@@ -981,6 +983,8 @@ struct redisServer {
unsigned long long maxmemory; /* Max number of memory bytes to use */
int maxmemory_policy; /* Policy for key eviction */
int maxmemory_samples; /* Pricision of random sampling */
+ unsigned int lfu_log_factor; /* LFU logarithmic counter factor. */
+ unsigned int lfu_decay_time; /* LFU counter decay factor. */
/* Blocked clients */
unsigned int bpop_blocked_clients; /* Number of clients blocked by lists */
list *unblocked_clients; /* list of clients to unblock before next loop */