command lookup process turned into a much more flexible and probably faster hash table

This commit is contained in:
antirez 2010-11-03 11:23:59 +01:00
parent c58bcfd350
commit 5d33a8862a
6 changed files with 70 additions and 28 deletions

View File

@ -435,16 +435,14 @@ time_t getExpire(redisDb *db, robj *key) {
* will be consistent even if we allow write operations against expiring * will be consistent even if we allow write operations against expiring
* keys. */ * keys. */
void propagateExpire(redisDb *db, robj *key) { void propagateExpire(redisDb *db, robj *key) {
struct redisCommand *cmd;
robj *argv[2]; robj *argv[2];
cmd = lookupCommand("del");
argv[0] = createStringObject("DEL",3); argv[0] = createStringObject("DEL",3);
argv[1] = key; argv[1] = key;
incrRefCount(key); incrRefCount(key);
if (server.appendonly) if (server.appendonly)
feedAppendOnlyFile(cmd,db->id,argv,2); feedAppendOnlyFile(server.delCommand,db->id,argv,2);
if (listLength(server.slaves)) if (listLength(server.slaves))
replicationFeedSlaves(server.slaves,db->id,argv,2); replicationFeedSlaves(server.slaves,db->id,argv,2);

View File

@ -42,6 +42,7 @@
#include <assert.h> #include <assert.h>
#include <limits.h> #include <limits.h>
#include <sys/time.h> #include <sys/time.h>
#include <ctype.h>
#include "dict.h" #include "dict.h"
#include "zmalloc.h" #include "zmalloc.h"
@ -94,6 +95,15 @@ unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
return hash; return hash;
} }
/* And a case insensitive version */
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {
unsigned int hash = 5381;
while (len--)
hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
return hash;
}
/* ----------------------------- API implementation ------------------------- */ /* ----------------------------- API implementation ------------------------- */
/* Reset an hashtable already initialized with ht_init(). /* Reset an hashtable already initialized with ht_init().

View File

@ -137,6 +137,7 @@ void dictReleaseIterator(dictIterator *iter);
dictEntry *dictGetRandomKey(dict *d); dictEntry *dictGetRandomKey(dict *d);
void dictPrintStats(dict *d); void dictPrintStats(dict *d);
unsigned int dictGenHashFunction(const unsigned char *buf, int len); unsigned int dictGenHashFunction(const unsigned char *buf, int len);
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len);
void dictEmpty(dict *d); void dictEmpty(dict *d);
void dictEnableResize(void); void dictEnableResize(void);
void dictDisableResize(void); void dictDisableResize(void);

View File

@ -65,12 +65,10 @@ void discardCommand(redisClient *c) {
/* Send a MULTI command to all the slaves and AOF file. Check the execCommand /* Send a MULTI command to all the slaves and AOF file. Check the execCommand
* implememntation for more information. */ * implememntation for more information. */
void execCommandReplicateMulti(redisClient *c) { void execCommandReplicateMulti(redisClient *c) {
struct redisCommand *cmd;
robj *multistring = createStringObject("MULTI",5); robj *multistring = createStringObject("MULTI",5);
cmd = lookupCommand("multi");
if (server.appendonly) if (server.appendonly)
feedAppendOnlyFile(cmd,c->db->id,&multistring,1); feedAppendOnlyFile(server.multiCommand,c->db->id,&multistring,1);
if (listLength(server.slaves)) if (listLength(server.slaves))
replicationFeedSlaves(server.slaves,c->db->id,&multistring,1); replicationFeedSlaves(server.slaves,c->db->id,&multistring,1);
decrRefCount(multistring); decrRefCount(multistring);

View File

@ -252,6 +252,15 @@ int dictSdsKeyCompare(void *privdata, const void *key1,
return memcmp(key1, key2, l1) == 0; return memcmp(key1, key2, l1) == 0;
} }
/* A case insensitive version used for the command lookup table. */
int dictSdsKeyCaseCompare(void *privdata, const void *key1,
const void *key2)
{
DICT_NOTUSED(privdata);
return strcasecmp(key1, key2) == 0;
}
void dictRedisObjectDestructor(void *privdata, void *val) void dictRedisObjectDestructor(void *privdata, void *val)
{ {
DICT_NOTUSED(privdata); DICT_NOTUSED(privdata);
@ -283,6 +292,10 @@ unsigned int dictSdsHash(const void *key) {
return dictGenHashFunction((unsigned char*)key, sdslen((char*)key)); return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));
} }
unsigned int dictSdsCaseHash(const void *key) {
return dictGenCaseHashFunction((unsigned char*)key, sdslen((char*)key));
}
int dictEncObjKeyCompare(void *privdata, const void *key1, int dictEncObjKeyCompare(void *privdata, const void *key1,
const void *key2) const void *key2)
{ {
@ -364,6 +377,16 @@ dictType keyptrDictType = {
NULL /* val destructor */ NULL /* val destructor */
}; };
/* Command table. sds string -> command struct pointer. */
dictType commandTableDictType = {
dictSdsCaseHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictSdsKeyCaseCompare, /* key compare */
dictSdsDestructor, /* key destructor */
NULL /* val destructor */
};
/* Hash type hash table (note that small hashes are represented with zimpaps) */ /* Hash type hash table (note that small hashes are represented with zimpaps) */
dictType hashDictType = { dictType hashDictType = {
dictEncObjHash, /* hash function */ dictEncObjHash, /* hash function */
@ -791,6 +814,12 @@ void initServer() {
redisLog(REDIS_WARNING, "Can't open /dev/null: %s", server.neterr); redisLog(REDIS_WARNING, "Can't open /dev/null: %s", server.neterr);
exit(1); exit(1);
} }
server.commands = dictCreate(&commandTableDictType,NULL);
populateCommandTable();
server.delCommand = lookupCommandByCString("del");
server.multiCommand = lookupCommandByCString("multi");
server.clients = listCreate(); server.clients = listCreate();
server.slaves = listCreate(); server.slaves = listCreate();
server.monitors = listCreate(); server.monitors = listCreate();
@ -860,31 +889,34 @@ void initServer() {
if (server.vm_enabled) vmInit(); if (server.vm_enabled) vmInit();
} }
int qsortRedisCommands(const void *r1, const void *r2) { /* Populates the Redis Command Table starting from the hard coded list
return strcasecmp( * we have on top of redis.c file. */
((struct redisCommand*)r1)->name, void populateCommandTable(void) {
((struct redisCommand*)r2)->name); int j;
} int numcommands = sizeof(readonlyCommandTable)/sizeof(struct redisCommand);
void sortCommandTable() { for (j = 0; j < numcommands; j++) {
/* Copy and sort the read-only version of the command table */ struct redisCommand *c = readonlyCommandTable+j;
commandTable = (struct redisCommand*)zmalloc(sizeof(readonlyCommandTable)); int retval;
memcpy(commandTable,readonlyCommandTable,sizeof(readonlyCommandTable));
qsort(commandTable, retval = dictAdd(server.commands, sdsnew(c->name), c);
sizeof(readonlyCommandTable)/sizeof(struct redisCommand), assert(retval == DICT_OK);
sizeof(struct redisCommand),qsortRedisCommands); }
} }
/* ====================== Commands lookup and execution ===================== */ /* ====================== Commands lookup and execution ===================== */
struct redisCommand *lookupCommand(char *name) { struct redisCommand *lookupCommand(sds name) {
struct redisCommand tmp = {name,NULL,0,0,NULL,0,0,0}; return dictFetchValue(server.commands, name);
return bsearch( }
&tmp,
commandTable, struct redisCommand *lookupCommandByCString(char *s) {
sizeof(readonlyCommandTable)/sizeof(struct redisCommand), struct redisCommand *cmd;
sizeof(struct redisCommand), sds name = sdsnew(s);
qsortRedisCommands);
cmd = dictFetchValue(server.commands, name);
sdsfree(name);
return cmd;
} }
/* Call() is the core of Redis execution of a command */ /* Call() is the core of Redis execution of a command */
@ -1443,7 +1475,6 @@ int main(int argc, char **argv) {
time_t start; time_t start;
initServerConfig(); initServerConfig();
sortCommandTable();
if (argc == 2) { if (argc == 2) {
if (strcmp(argv[1], "-v") == 0 || if (strcmp(argv[1], "-v") == 0 ||
strcmp(argv[1], "--version") == 0) version(); strcmp(argv[1], "--version") == 0) version();

View File

@ -360,6 +360,8 @@ struct redisServer {
long long dirty; /* changes to DB from the last save */ long long dirty; /* changes to DB from the last save */
long long dirty_before_bgsave; /* used to restore dirty on failed BGSAVE */ long long dirty_before_bgsave; /* used to restore dirty on failed BGSAVE */
list *clients; list *clients;
dict *commands; /* Command table hahs table */
struct redisCommand *delCommand, *multiCommand; /* often lookedup cmds */
list *slaves, *monitors; list *slaves, *monitors;
char neterr[ANET_ERR_LEN]; char neterr[ANET_ERR_LEN];
aeEventLoop *el; aeEventLoop *el;
@ -746,7 +748,8 @@ zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj);
void freeMemoryIfNeeded(void); void freeMemoryIfNeeded(void);
int processCommand(redisClient *c); int processCommand(redisClient *c);
void setupSigSegvAction(void); void setupSigSegvAction(void);
struct redisCommand *lookupCommand(char *name); struct redisCommand *lookupCommand(sds name);
struct redisCommand *lookupCommandByCString(char *s);
void call(redisClient *c, struct redisCommand *cmd); void call(redisClient *c, struct redisCommand *cmd);
int prepareForShutdown(); int prepareForShutdown();
void redisLog(int level, const char *fmt, ...); void redisLog(int level, const char *fmt, ...);
@ -754,6 +757,7 @@ void usage();
void updateDictResizePolicy(void); void updateDictResizePolicy(void);
int htNeedsResize(dict *dict); int htNeedsResize(dict *dict);
void oom(const char *msg); void oom(const char *msg);
void populateCommandTable(void);
/* Virtual Memory */ /* Virtual Memory */
void vmInit(void); void vmInit(void);