}
}
+void updateLRUClock(void) {
+ server.lruclock = (time(NULL)/REDIS_LRU_CLOCK_RESOLUTION) &
+ REDIS_LRU_CLOCK_MAX;
+}
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
int j, loops = server.cronloops++;
* in objects at every object access, and accuracy is not needed.
* To access a global var is faster than calling time(NULL) */
server.unixtime = time(NULL);
- /* We have just 21 bits per object for LRU information.
- * So we use an (eventually wrapping) LRU clock with minutes resolution.
+ /* We have just 22 bits per object for LRU information.
+ * So we use an (eventually wrapping) LRU clock with 10 seconds resolution.
+ * 2^22 bits with 10 seconds resoluton is more or less 1.5 years.
*
- * When we need to select what object to swap, we compute the minimum
- * time distance between the current lruclock and the object last access
- * lruclock info. Even if clocks will wrap on overflow, there is
- * the interesting property that we are sure that at least
- * ABS(A-B) minutes passed between current time and timestamp B.
+ * Note that even if this will wrap after 1.5 years it's not a problem,
+ * everything will still work but just some object will appear younger
+ * to Redis. But for this to happen a given object should never be touched
+ * for 1.5 years.
*
- * This is not precise but we don't need at all precision, but just
- * something statistically reasonable.
+ * Note that you can change the resolution altering the
+ * REDIS_LRU_CLOCK_RESOLUTION define.
*/
- server.lruclock = (time(NULL)/60)&((1<<21)-1);
+ updateLRUClock();
/* We received a SIGTERM, shutting down here in a safe way, as it is
* not ok doing so inside the signal handler. */
server.maxclients = 0;
server.blpop_blocked_clients = 0;
server.maxmemory = 0;
+ server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
+ server.maxmemory_samples = 3;
server.vm_enabled = 0;
server.vm_swap_file = zstrdup("/tmp/redis-%p.vm");
server.vm_page_size = 256; /* 256 bytes per page */
server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES;
server.shutdown_asap = 0;
+ updateLRUClock();
resetServerSaveParams();
appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */
server.stat_numconnections = 0;
server.stat_expiredkeys = 0;
server.stat_starttime = time(NULL);
+ server.stat_keyspace_misses = 0;
+ server.stat_keyspace_hits = 0;
server.unixtime = time(NULL);
aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL);
if (aeCreateFileEvent(server.el, server.fd, AE_READABLE,
/* Append only file: fsync() the AOF and exit */
aof_fsync(server.appendfd);
if (server.vm_enabled) unlink(server.vm_swap_file);
- } else {
+ } else if (server.saveparamslen > 0) {
/* Snapshotting. Perform a SYNC SAVE and exit */
if (rdbSave(server.dbfilename) != REDIS_OK) {
/* Ooops.. error saving! The best we can do is to continue
redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit");
return REDIS_ERR;
}
+ } else {
+ redisLog(REDIS_WARNING,"Not saving DB.");
}
if (server.daemonize) unlink(server.pidfile);
redisLog(REDIS_WARNING,"Server exit now, bye bye...");
"process_id:%ld\r\n"
"uptime_in_seconds:%ld\r\n"
"uptime_in_days:%ld\r\n"
+ "lru_clock:%ld\r\n"
"used_cpu_sys:%.2f\r\n"
"used_cpu_user:%.2f\r\n"
"used_cpu_sys_childrens:%.2f\r\n"
"used_memory:%zu\r\n"
"used_memory_human:%s\r\n"
"mem_fragmentation_ratio:%.2f\r\n"
+ "use_tcmalloc:%d\r\n"
"changes_since_last_save:%lld\r\n"
"bgsave_in_progress:%d\r\n"
"last_save_time:%ld\r\n"
"total_connections_received:%lld\r\n"
"total_commands_processed:%lld\r\n"
"expired_keys:%lld\r\n"
+ "keyspace_hits:%lld\r\n"
+ "keyspace_misses:%lld\r\n"
"hash_max_zipmap_entries:%zu\r\n"
"hash_max_zipmap_value:%zu\r\n"
"pubsub_channels:%ld\r\n"
(long) getpid(),
uptime,
uptime/(3600*24),
+ (unsigned long) server.lruclock,
(float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
(float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
(float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
zmalloc_used_memory(),
hmem,
zmalloc_get_fragmentation_ratio(),
+#ifdef USE_TCMALLOC
+ 1,
+#else
+ 0,
+#endif
server.dirty,
server.bgsavechildpid != -1,
server.lastsave,
server.stat_numconnections,
server.stat_numcommands,
server.stat_expiredkeys,
+ server.stat_keyspace_hits,
+ server.stat_keyspace_misses,
server.hash_max_zipmap_entries,
server.hash_max_zipmap_value,
dictSize(server.pubsub_channels),
* memory usage.
*/
void freeMemoryIfNeeded(void) {
+ /* Remove keys accordingly to the active policy as long as we are
+ * over the memory limit. */
while (server.maxmemory && zmalloc_used_memory() > server.maxmemory) {
int j, k, freed = 0;
+ /* Basic strategy -- remove objects from the free list. */
if (tryFreeOneObjectFromFreelist() == REDIS_OK) continue;
+
+ for (j = 0; j < server.dbnum; j++) {
+ long bestval;
+ sds bestkey = NULL;
+ struct dictEntry *de;
+ redisDb *db = server.db+j;
+ dict *dict;
+
+ if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
+ server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM)
+ {
+ dict = server.db[j].dict;
+ } else {
+ dict = server.db[j].expires;
+ }
+ if (dictSize(dict) == 0) continue;
+
+ /* volatile-random and allkeys-random policy */
+ if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM ||
+ server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM)
+ {
+ de = dictGetRandomKey(dict);
+ bestkey = dictGetEntryKey(de);
+ }
+
+ /* volatile-lru and allkeys-lru policy */
+ else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
+ server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
+ {
+ for (k = 0; k < server.maxmemory_samples; k++) {
+ sds thiskey;
+ long thisval;
+ robj *o;
+
+ de = dictGetRandomKey(dict);
+ thiskey = dictGetEntryKey(de);
+ o = dictGetEntryVal(de);
+ thisval = estimateObjectIdleTime(o);
+
+ /* Higher idle time is better candidate for deletion */
+ if (bestkey == NULL || thisval > bestval) {
+ bestkey = thiskey;
+ bestval = thisval;
+ }
+ }
+ }
+
+ /* volatile-ttl */
+ else if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_TTL) {
+ for (k = 0; k < server.maxmemory_samples; k++) {
+ sds thiskey;
+ long thisval;
+
+ de = dictGetRandomKey(dict);
+ thiskey = dictGetEntryKey(de);
+ thisval = (long) dictGetEntryVal(de);
+
+ /* Expire sooner (minor expire unix timestamp) is better
+ * candidate for deletion */
+ if (bestkey == NULL || thisval < bestval) {
+ bestkey = thiskey;
+ bestval = thisval;
+ }
+ }
+ }
+
+ /* Finally remove the selected key. */
+ if (bestkey) {
+ robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
+ dbDelete(db,keyobj);
+ server.stat_expiredkeys++;
+ decrRefCount(keyobj);
+ freed++;
+ }
+ }
+ if (!freed) return; /* nothing to free... */
+ }
+
+ while(0) {
+ int j, k, freed = 0;
for (j = 0; j < server.dbnum; j++) {
int minttl = -1;
sds minkey = NULL;