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];
key = dictGetKey(de);
/* If the dictionary we are sampling from is not the main
* dictionary (but the expires one) we need to lookup the key
* again in the key dictionary to obtain the value object. */
if (sampledict != keydict) de = dictFind(keydict, key);
o = dictGetVal(de);
if (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {
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) {
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
* so that we expire keys starting from greater idle time.
* 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 of 255. */
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.
@ -377,7 +389,9 @@ int freeMemoryIfNeeded(void) {
dict *dict;
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;
while(bestkey == NULL) {
@ -388,8 +402,7 @@ int freeMemoryIfNeeded(void) {
* every DB. */
for (i = 0; i < server.dbnum; i++) {
db = server.db+i;
dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU ||
server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU) ?
dict = (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ?
db->dict : db->expires;
if ((keys = dictSize(dict)) != 0) {
evictionPoolPopulate(i, dict, db->dict, pool);
@ -403,9 +416,7 @@ int freeMemoryIfNeeded(void) {
if (pool[k].key == NULL) continue;
bestdbid = pool[k].dbid;
if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU ||
server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU)
{
if (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {
de = dictFind(server.db[pool[k].dbid].dict,
pool[k].key);
} 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. */
if (bestkey) {
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. */
#define MAXMEMORY_FLAG_LRU (1<<0)
#define MAXMEMORY_FLAG_LFU (1<<1)
#define MAXMEMORY_FLAG_ALLKEYS (1<<2)
#define MAXMEMORY_FLAG_NO_SHARED_INTEGERS \
(MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU)
#define MAXMEMORY_VOLATILE_LRU ((0<<8)|MAXMEMORY_FLAG_LRU)
#define MAXMEMORY_VOLATILE_LFU ((1<<8)|MAXMEMORY_FLAG_LFU)
#define MAXMEMORY_VOLATILE_TTL (2<<8)
#define MAXMEMORY_VOLATILE_RANDOM (3<<8)
#define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU)
#define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU)
#define MAXMEMORY_ALLKEYS_RANDOM (6<<8)
#define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_ALLKEYS)
#define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU|MAXMEMORY_FLAG_ALLKEYS)
#define MAXMEMORY_ALLKEYS_RANDOM ((6<<8)|MAXMEMORY_FLAG_ALLKEYS)
#define MAXMEMORY_NO_EVICTION (7<<8)
#define CONFIG_DEFAULT_MAXMEMORY_POLICY MAXMEMORY_NO_EVICTION