mirror of
https://github.com/fluencelabs/redis
synced 2025-03-19 17:10:50 +00:00
Active rehashing
This commit is contained in:
parent
5413c40da7
commit
8ca3e9d10b
38
dict.c
38
dict.c
@ -41,6 +41,7 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include "dict.h"
|
#include "dict.h"
|
||||||
#include "zmalloc.h"
|
#include "zmalloc.h"
|
||||||
@ -237,6 +238,25 @@ int dictRehash(dict *d, int n) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long long timeInMilliseconds(void) {
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
gettimeofday(&tv,NULL);
|
||||||
|
return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */
|
||||||
|
int dictRehashMilliseconds(dict *d, int ms) {
|
||||||
|
long long start = timeInMilliseconds();
|
||||||
|
int rehashes = 0;
|
||||||
|
|
||||||
|
while(dictRehash(d,100)) {
|
||||||
|
rehashes += 100;
|
||||||
|
if (timeInMilliseconds()-start > ms) break;
|
||||||
|
}
|
||||||
|
return rehashes;
|
||||||
|
}
|
||||||
|
|
||||||
/* This function performs just a step of rehashing, and only if there are
|
/* This function performs just a step of rehashing, and only if there are
|
||||||
* not iterators bound to our hash table. When we have iterators in the middle
|
* not iterators bound to our hash table. When we have iterators in the middle
|
||||||
* of a rehashing we can't mess with the two hash tables otherwise some element
|
* of a rehashing we can't mess with the two hash tables otherwise some element
|
||||||
@ -527,7 +547,7 @@ static unsigned long _dictNextPower(unsigned long size)
|
|||||||
* index is always returned in the context of the second (new) hash table. */
|
* index is always returned in the context of the second (new) hash table. */
|
||||||
static int _dictKeyIndex(dict *d, const void *key)
|
static int _dictKeyIndex(dict *d, const void *key)
|
||||||
{
|
{
|
||||||
unsigned int h, h1, h2;
|
unsigned int h, idx, table;
|
||||||
dictEntry *he;
|
dictEntry *he;
|
||||||
|
|
||||||
/* Expand the hashtable if needed */
|
/* Expand the hashtable if needed */
|
||||||
@ -535,24 +555,18 @@ static int _dictKeyIndex(dict *d, const void *key)
|
|||||||
return -1;
|
return -1;
|
||||||
/* Compute the key hash value */
|
/* Compute the key hash value */
|
||||||
h = dictHashKey(d, key);
|
h = dictHashKey(d, key);
|
||||||
h1 = h & d->ht[0].sizemask;
|
for (table = 0; table <= 1; table++) {
|
||||||
h2 = h & d->ht[1].sizemask;
|
idx = h & d->ht[table].sizemask;
|
||||||
/* Search if this slot does not already contain the given key */
|
/* Search if this slot does not already contain the given key */
|
||||||
he = d->ht[0].table[h1];
|
he = d->ht[table].table[idx];
|
||||||
while(he) {
|
while(he) {
|
||||||
if (dictCompareHashKeys(d, key, he->key))
|
if (dictCompareHashKeys(d, key, he->key))
|
||||||
return -1;
|
return -1;
|
||||||
he = he->next;
|
he = he->next;
|
||||||
}
|
}
|
||||||
if (!dictIsRehashing(d)) return h1;
|
if (!dictIsRehashing(d)) break;
|
||||||
/* Check the second hash table */
|
|
||||||
he = d->ht[1].table[h2];
|
|
||||||
while(he) {
|
|
||||||
if (dictCompareHashKeys(d, key, he->key))
|
|
||||||
return -1;
|
|
||||||
he = he->next;
|
|
||||||
}
|
}
|
||||||
return h2;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dictEmpty(dict *d) {
|
void dictEmpty(dict *d) {
|
||||||
|
25
dict.h
25
dict.h
@ -122,24 +122,25 @@ typedef struct dictIterator {
|
|||||||
|
|
||||||
/* API */
|
/* API */
|
||||||
dict *dictCreate(dictType *type, void *privDataPtr);
|
dict *dictCreate(dictType *type, void *privDataPtr);
|
||||||
int dictExpand(dict *ht, unsigned long size);
|
int dictExpand(dict *d, unsigned long size);
|
||||||
int dictAdd(dict *ht, void *key, void *val);
|
int dictAdd(dict *d, void *key, void *val);
|
||||||
int dictReplace(dict *ht, void *key, void *val);
|
int dictReplace(dict *d, void *key, void *val);
|
||||||
int dictDelete(dict *ht, const void *key);
|
int dictDelete(dict *d, const void *key);
|
||||||
int dictDeleteNoFree(dict *ht, const void *key);
|
int dictDeleteNoFree(dict *d, const void *key);
|
||||||
void dictRelease(dict *ht);
|
void dictRelease(dict *d);
|
||||||
dictEntry * dictFind(dict *ht, const void *key);
|
dictEntry * dictFind(dict *d, const void *key);
|
||||||
int dictResize(dict *ht);
|
int dictResize(dict *d);
|
||||||
dictIterator *dictGetIterator(dict *ht);
|
dictIterator *dictGetIterator(dict *d);
|
||||||
dictEntry *dictNext(dictIterator *iter);
|
dictEntry *dictNext(dictIterator *iter);
|
||||||
void dictReleaseIterator(dictIterator *iter);
|
void dictReleaseIterator(dictIterator *iter);
|
||||||
dictEntry *dictGetRandomKey(dict *ht);
|
dictEntry *dictGetRandomKey(dict *d);
|
||||||
void dictPrintStats(dict *ht);
|
void dictPrintStats(dict *d);
|
||||||
unsigned int dictGenHashFunction(const unsigned char *buf, int len);
|
unsigned int dictGenHashFunction(const unsigned char *buf, int len);
|
||||||
void dictEmpty(dict *ht);
|
void dictEmpty(dict *d);
|
||||||
void dictEnableResize(void);
|
void dictEnableResize(void);
|
||||||
void dictDisableResize(void);
|
void dictDisableResize(void);
|
||||||
int dictRehash(dict *d, int n);
|
int dictRehash(dict *d, int n);
|
||||||
|
int dictRehashMilliseconds(dict *d, int ms);
|
||||||
|
|
||||||
/* Hash table types */
|
/* Hash table types */
|
||||||
extern dictType dictTypeHeapStringCopyKey;
|
extern dictType dictTypeHeapStringCopyKey;
|
||||||
|
30
redis.c
30
redis.c
@ -91,7 +91,7 @@
|
|||||||
#define REDIS_CONFIGLINE_MAX 1024
|
#define REDIS_CONFIGLINE_MAX 1024
|
||||||
#define REDIS_OBJFREELIST_MAX 1000000 /* Max number of objects to cache */
|
#define REDIS_OBJFREELIST_MAX 1000000 /* Max number of objects to cache */
|
||||||
#define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */
|
#define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */
|
||||||
#define REDIS_EXPIRELOOKUPS_PER_CRON 10 /* try to expire 10 keys/loop */
|
#define REDIS_EXPIRELOOKUPS_PER_CRON 10 /* lookup 10 expires per loop */
|
||||||
#define REDIS_MAX_WRITE_PER_EVENT (1024*64)
|
#define REDIS_MAX_WRITE_PER_EVENT (1024*64)
|
||||||
#define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */
|
#define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */
|
||||||
|
|
||||||
@ -379,6 +379,7 @@ struct redisServer {
|
|||||||
char *requirepass;
|
char *requirepass;
|
||||||
int shareobjects;
|
int shareobjects;
|
||||||
int rdbcompression;
|
int rdbcompression;
|
||||||
|
int activerehashing;
|
||||||
/* Replication related */
|
/* Replication related */
|
||||||
int isslave;
|
int isslave;
|
||||||
char *masterauth;
|
char *masterauth;
|
||||||
@ -1208,6 +1209,21 @@ static void tryResizeHashTables(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Our hash table implementation performs rehashing incrementally while
|
||||||
|
* we write/read from the hash table. Still if the server is idle, the hash
|
||||||
|
* table will use two tables for a long time. So we try to use 1 millisecond
|
||||||
|
* of CPU time at every serverCron() loop in order to rehash some key. */
|
||||||
|
static void incrementallyRehash(void) {
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = 0; j < server.dbnum; j++) {
|
||||||
|
if (dictIsRehashing(server.db[j].dict)) {
|
||||||
|
dictRehashMilliseconds(server.db[j].dict,1);
|
||||||
|
break; /* already used our millisecond for this loop... */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* A background saving child (BGSAVE) terminated its work. Handle this. */
|
/* A background saving child (BGSAVE) terminated its work. Handle this. */
|
||||||
void backgroundSaveDoneHandler(int statloc) {
|
void backgroundSaveDoneHandler(int statloc) {
|
||||||
int exitcode = WEXITSTATUS(statloc);
|
int exitcode = WEXITSTATUS(statloc);
|
||||||
@ -1337,10 +1353,9 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
|
|||||||
* if we resize the HT while there is the saving child at work actually
|
* if we resize the HT while there is the saving child at work actually
|
||||||
* a lot of memory movements in the parent will cause a lot of pages
|
* a lot of memory movements in the parent will cause a lot of pages
|
||||||
* copied. */
|
* copied. */
|
||||||
if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1 &&
|
if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1) {
|
||||||
!(loops % 10))
|
if (!(loops % 10)) tryResizeHashTables();
|
||||||
{
|
if (server.activerehashing) incrementallyRehash();
|
||||||
tryResizeHashTables();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show information about connected clients */
|
/* Show information about connected clients */
|
||||||
@ -1568,6 +1583,7 @@ static void initServerConfig() {
|
|||||||
server.requirepass = NULL;
|
server.requirepass = NULL;
|
||||||
server.shareobjects = 0;
|
server.shareobjects = 0;
|
||||||
server.rdbcompression = 1;
|
server.rdbcompression = 1;
|
||||||
|
server.activerehashing = 1;
|
||||||
server.maxclients = 0;
|
server.maxclients = 0;
|
||||||
server.blpop_blocked_clients = 0;
|
server.blpop_blocked_clients = 0;
|
||||||
server.maxmemory = 0;
|
server.maxmemory = 0;
|
||||||
@ -1801,6 +1817,10 @@ static void loadServerConfig(char *filename) {
|
|||||||
if ((server.rdbcompression = yesnotoi(argv[1])) == -1) {
|
if ((server.rdbcompression = yesnotoi(argv[1])) == -1) {
|
||||||
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
||||||
}
|
}
|
||||||
|
} else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
|
||||||
|
if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
|
||||||
|
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
||||||
|
}
|
||||||
} else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
|
} else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
|
||||||
if ((server.daemonize = yesnotoi(argv[1])) == -1) {
|
if ((server.daemonize = yesnotoi(argv[1])) == -1) {
|
||||||
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
||||||
|
20
redis.conf
20
redis.conf
@ -262,6 +262,26 @@ glueoutputbuf yes
|
|||||||
hash-max-zipmap-entries 64
|
hash-max-zipmap-entries 64
|
||||||
hash-max-zipmap-value 512
|
hash-max-zipmap-value 512
|
||||||
|
|
||||||
|
# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in
|
||||||
|
# order to help rehashing the main Redis hash table (the one mapping top-level
|
||||||
|
# keys to values). The hash table implementation redis uses (see dict.c)
|
||||||
|
# performs a lazy rehashing: the more operation you run into an hash table
|
||||||
|
# that is rhashing, the more rehashing "steps" are performed, so if the
|
||||||
|
# server is idle the rehashing is never complete and some more memory is used
|
||||||
|
# by the hash table.
|
||||||
|
#
|
||||||
|
# The default is to use this millisecond 10 times every second in order to
|
||||||
|
# active rehashing the main dictionaries, freeing memory when possible.
|
||||||
|
#
|
||||||
|
# If unsure:
|
||||||
|
# use "activerehashing no" if you have hard latency requirements and it is
|
||||||
|
# not a good thing in your environment that Redis can reply form time to time
|
||||||
|
# to queries with 2 milliseconds delay.
|
||||||
|
#
|
||||||
|
# use "activerehashing yes" if you don't have such hard requirements but
|
||||||
|
# want to free memory asap when possible.
|
||||||
|
activerehashing yes
|
||||||
|
|
||||||
################################## INCLUDES ###################################
|
################################## INCLUDES ###################################
|
||||||
|
|
||||||
# Include one or more other config files here. This is useful if you
|
# Include one or more other config files here. This is useful if you
|
||||||
|
Loading…
x
Reference in New Issue
Block a user