Volatile-ttl eviction policy implemented in terms of the pool.

Precision of the eviction improved sensibly. Also this allows us to have
a single code path for most eviction types.
This commit is contained in:
antirez 2016-07-20 19:53:27 +02:00
parent 9f1b7ab2ed
commit 2d5eb1f1a0
2 changed files with 25 additions and 48 deletions

View File

@ -159,14 +159,21 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
de = samples[j]; de = samples[j];
key = dictGetKey(de); key = dictGetKey(de);
/* If the dictionary we are sampling from is not the main /* If the dictionary we are sampling from is not the main
* dictionary (but the expires one) we need to lookup the key * dictionary (but the expires one) we need to lookup the key
* again in the key dictionary to obtain the value object. */ * again in the key dictionary to obtain the value object. */
if (sampledict != keydict) de = dictFind(keydict, key); if (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {
o = dictGetVal(de); if (sampledict != keydict) de = dictFind(keydict, key);
o = dictGetVal(de);
}
/* Calculate the idle time according to the policy. This is called
* idle just because the code initially handled LRU, but is in fact
* just a score where an higher score means better candidate. */
if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) { if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {
idle = estimateObjectIdleTime(o); idle = estimateObjectIdleTime(o);
} else { } else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
/* When we use an LRU policy, we sort the keys by idle time /* When we use an LRU policy, we sort the keys by idle time
* so that we expire keys starting from greater idle time. * so that we expire keys starting from greater idle time.
* However when the policy is an LFU one, we have a frequency * However when the policy is an LFU one, we have a frequency
@ -175,6 +182,11 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
* frequency subtracting the actual frequency to the maximum * frequency subtracting the actual frequency to the maximum
* frequency of 255. */ * frequency of 255. */
idle = 255-LFUDecrAndReturn(o); idle = 255-LFUDecrAndReturn(o);
} else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
/* In this case the sooner the expire the better. */
idle = ULLONG_MAX - (long)dictGetVal(de);
} else {
serverPanic("Unknown eviction policy in evictionPoolPopulate()");
} }
/* Insert the element inside the pool. /* Insert the element inside the pool.
@ -377,7 +389,9 @@ int freeMemoryIfNeeded(void) {
dict *dict; dict *dict;
dictEntry *de; dictEntry *de;
if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU)) { if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) ||
server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL)
{
struct evictionPoolEntry *pool = EvictionPoolLRU; struct evictionPoolEntry *pool = EvictionPoolLRU;
while(bestkey == NULL) { while(bestkey == NULL) {
@ -388,8 +402,7 @@ int freeMemoryIfNeeded(void) {
* every DB. */ * every DB. */
for (i = 0; i < server.dbnum; i++) { for (i = 0; i < server.dbnum; i++) {
db = server.db+i; db = server.db+i;
dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU || dict = (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ?
server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU) ?
db->dict : db->expires; db->dict : db->expires;
if ((keys = dictSize(dict)) != 0) { if ((keys = dictSize(dict)) != 0) {
evictionPoolPopulate(i, dict, db->dict, pool); evictionPoolPopulate(i, dict, db->dict, pool);
@ -403,9 +416,7 @@ int freeMemoryIfNeeded(void) {
if (pool[k].key == NULL) continue; if (pool[k].key == NULL) continue;
bestdbid = pool[k].dbid; bestdbid = pool[k].dbid;
if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU || if (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {
server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU)
{
de = dictFind(server.db[pool[k].dbid].dict, de = dictFind(server.db[pool[k].dbid].dict,
pool[k].key); pool[k].key);
} else { } else {
@ -452,42 +463,6 @@ int freeMemoryIfNeeded(void) {
} }
} }
/* volatile-ttl */
else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
long bestttl = 0; /* Initialized to avoid warning. */
/* In this policy we scan a single DB per iteration (visiting
* a different DB per call), expiring the key with the smallest
* TTL among the few sampled.
*
* Note that this algorithm makes local-DB choices, and should
* use a pool and code more similr to the one used in the
* LRU eviction policies in the future. */
for (i = 0; i < server.dbnum; i++) {
j = (++next_db) % server.dbnum;
db = server.db+j;
dict = db->expires;
if (dictSize(dict) != 0) {
for (k = 0; k < server.maxmemory_samples; k++) {
sds thiskey;
long thisttl;
de = dictGetRandomKey(dict);
thiskey = dictGetKey(de);
thisttl = (long) dictGetVal(de);
/* Keys expiring sooner (smaller unix timestamp) are
* better candidates for deletion */
if (bestkey == NULL || thisttl < bestttl) {
bestkey = thiskey;
bestttl = thisttl;
bestdbid = j;
}
}
}
}
}
/* Finally remove the selected key. */ /* Finally remove the selected key. */
if (bestkey) { if (bestkey) {
db = server.db+bestdbid; db = server.db+bestdbid;

View File

@ -348,15 +348,17 @@ typedef long long mstime_t; /* millisecond time type. */
* properties common to multiple policies is faster. */ * properties common to multiple policies is faster. */
#define MAXMEMORY_FLAG_LRU (1<<0) #define MAXMEMORY_FLAG_LRU (1<<0)
#define MAXMEMORY_FLAG_LFU (1<<1) #define MAXMEMORY_FLAG_LFU (1<<1)
#define MAXMEMORY_FLAG_ALLKEYS (1<<2)
#define MAXMEMORY_FLAG_NO_SHARED_INTEGERS \ #define MAXMEMORY_FLAG_NO_SHARED_INTEGERS \
(MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU)
#define MAXMEMORY_VOLATILE_LRU ((0<<8)|MAXMEMORY_FLAG_LRU) #define MAXMEMORY_VOLATILE_LRU ((0<<8)|MAXMEMORY_FLAG_LRU)
#define MAXMEMORY_VOLATILE_LFU ((1<<8)|MAXMEMORY_FLAG_LFU) #define MAXMEMORY_VOLATILE_LFU ((1<<8)|MAXMEMORY_FLAG_LFU)
#define MAXMEMORY_VOLATILE_TTL (2<<8) #define MAXMEMORY_VOLATILE_TTL (2<<8)
#define MAXMEMORY_VOLATILE_RANDOM (3<<8) #define MAXMEMORY_VOLATILE_RANDOM (3<<8)
#define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU) #define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_ALLKEYS)
#define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU) #define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU|MAXMEMORY_FLAG_ALLKEYS)
#define MAXMEMORY_ALLKEYS_RANDOM (6<<8) #define MAXMEMORY_ALLKEYS_RANDOM ((6<<8)|MAXMEMORY_FLAG_ALLKEYS)
#define MAXMEMORY_NO_EVICTION (7<<8) #define MAXMEMORY_NO_EVICTION (7<<8)
#define CONFIG_DEFAULT_MAXMEMORY_POLICY MAXMEMORY_NO_EVICTION #define CONFIG_DEFAULT_MAXMEMORY_POLICY MAXMEMORY_NO_EVICTION