maxmemory implemented
This commit is contained in:
parent
16edf32dc3
commit
3fd78bcd45
109
redis.c
109
redis.c
@ -80,8 +80,13 @@
|
|||||||
#define REDIS_HT_MINSLOTS 16384 /* Never resize the HT under this */
|
#define REDIS_HT_MINSLOTS 16384 /* Never resize the HT under this */
|
||||||
|
|
||||||
/* Command flags */
|
/* Command flags */
|
||||||
#define REDIS_CMD_BULK 1
|
#define REDIS_CMD_BULK 1 /* Bulk write command */
|
||||||
#define REDIS_CMD_INLINE 2
|
#define REDIS_CMD_INLINE 2 /* Inline command */
|
||||||
|
/* REDIS_CMD_DENYOOM reserves a longer comment: all the commands marked with
|
||||||
|
this flags will return an error when the 'maxmemory' option is set in the
|
||||||
|
config file and the server is using more than maxmemory bytes of memory.
|
||||||
|
In short this commands are denied on low memory conditions. */
|
||||||
|
#define REDIS_CMD_DENYOOM 4
|
||||||
|
|
||||||
/* Object types */
|
/* Object types */
|
||||||
#define REDIS_STRING 0
|
#define REDIS_STRING 0
|
||||||
@ -246,6 +251,7 @@ struct redisServer {
|
|||||||
redisClient *master; /* client that is master for this slave */
|
redisClient *master; /* client that is master for this slave */
|
||||||
int replstate;
|
int replstate;
|
||||||
unsigned int maxclients;
|
unsigned int maxclients;
|
||||||
|
unsigned int maxmemory;
|
||||||
/* Sort parameters - qsort_r() is only available under BSD so we
|
/* Sort parameters - qsort_r() is only available under BSD so we
|
||||||
* have to take this state global, in order to pass it to sortCompare() */
|
* have to take this state global, in order to pass it to sortCompare() */
|
||||||
int sort_desc;
|
int sort_desc;
|
||||||
@ -307,6 +313,7 @@ static int deleteKey(redisDb *db, robj *key);
|
|||||||
static time_t getExpire(redisDb *db, robj *key);
|
static time_t getExpire(redisDb *db, robj *key);
|
||||||
static int setExpire(redisDb *db, robj *key, time_t when);
|
static int setExpire(redisDb *db, robj *key, time_t when);
|
||||||
static void updateSalvesWaitingBgsave(int bgsaveerr);
|
static void updateSalvesWaitingBgsave(int bgsaveerr);
|
||||||
|
static void freeMemoryIfNeeded(void);
|
||||||
|
|
||||||
static void authCommand(redisClient *c);
|
static void authCommand(redisClient *c);
|
||||||
static void pingCommand(redisClient *c);
|
static void pingCommand(redisClient *c);
|
||||||
@ -371,38 +378,38 @@ static void slaveofCommand(redisClient *c);
|
|||||||
static struct redisServer server; /* server global state */
|
static struct redisServer server; /* server global state */
|
||||||
static struct redisCommand cmdTable[] = {
|
static struct redisCommand cmdTable[] = {
|
||||||
{"get",getCommand,2,REDIS_CMD_INLINE},
|
{"get",getCommand,2,REDIS_CMD_INLINE},
|
||||||
{"set",setCommand,3,REDIS_CMD_BULK},
|
{"set",setCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
|
||||||
{"setnx",setnxCommand,3,REDIS_CMD_BULK},
|
{"setnx",setnxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
|
||||||
{"del",delCommand,-2,REDIS_CMD_INLINE},
|
{"del",delCommand,-2,REDIS_CMD_INLINE},
|
||||||
{"exists",existsCommand,2,REDIS_CMD_INLINE},
|
{"exists",existsCommand,2,REDIS_CMD_INLINE},
|
||||||
{"incr",incrCommand,2,REDIS_CMD_INLINE},
|
{"incr",incrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"decr",decrCommand,2,REDIS_CMD_INLINE},
|
{"decr",decrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"mget",mgetCommand,-2,REDIS_CMD_INLINE},
|
{"mget",mgetCommand,-2,REDIS_CMD_INLINE},
|
||||||
{"rpush",rpushCommand,3,REDIS_CMD_BULK},
|
{"rpush",rpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
|
||||||
{"lpush",lpushCommand,3,REDIS_CMD_BULK},
|
{"lpush",lpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
|
||||||
{"rpop",rpopCommand,2,REDIS_CMD_INLINE},
|
{"rpop",rpopCommand,2,REDIS_CMD_INLINE},
|
||||||
{"lpop",lpopCommand,2,REDIS_CMD_INLINE},
|
{"lpop",lpopCommand,2,REDIS_CMD_INLINE},
|
||||||
{"llen",llenCommand,2,REDIS_CMD_INLINE},
|
{"llen",llenCommand,2,REDIS_CMD_INLINE},
|
||||||
{"lindex",lindexCommand,3,REDIS_CMD_INLINE},
|
{"lindex",lindexCommand,3,REDIS_CMD_INLINE},
|
||||||
{"lset",lsetCommand,4,REDIS_CMD_BULK},
|
{"lset",lsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
|
||||||
{"lrange",lrangeCommand,4,REDIS_CMD_INLINE},
|
{"lrange",lrangeCommand,4,REDIS_CMD_INLINE},
|
||||||
{"ltrim",ltrimCommand,4,REDIS_CMD_INLINE},
|
{"ltrim",ltrimCommand,4,REDIS_CMD_INLINE},
|
||||||
{"lrem",lremCommand,4,REDIS_CMD_BULK},
|
{"lrem",lremCommand,4,REDIS_CMD_BULK},
|
||||||
{"sadd",saddCommand,3,REDIS_CMD_BULK},
|
{"sadd",saddCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
|
||||||
{"srem",sremCommand,3,REDIS_CMD_BULK},
|
{"srem",sremCommand,3,REDIS_CMD_BULK},
|
||||||
{"smove",smoveCommand,4,REDIS_CMD_BULK},
|
{"smove",smoveCommand,4,REDIS_CMD_BULK},
|
||||||
{"sismember",sismemberCommand,3,REDIS_CMD_BULK},
|
{"sismember",sismemberCommand,3,REDIS_CMD_BULK},
|
||||||
{"scard",scardCommand,2,REDIS_CMD_INLINE},
|
{"scard",scardCommand,2,REDIS_CMD_INLINE},
|
||||||
{"sinter",sinterCommand,-2,REDIS_CMD_INLINE},
|
{"sinter",sinterCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE},
|
{"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"sunion",sunionCommand,-2,REDIS_CMD_INLINE},
|
{"sunion",sunionCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"sunionstore",sunionstoreCommand,-3,REDIS_CMD_INLINE},
|
{"sunionstore",sunionstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"sdiff",sdiffCommand,-2,REDIS_CMD_INLINE},
|
{"sdiff",sdiffCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_INLINE},
|
{"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"smembers",sinterCommand,2,REDIS_CMD_INLINE},
|
{"smembers",sinterCommand,2,REDIS_CMD_INLINE},
|
||||||
{"incrby",incrbyCommand,3,REDIS_CMD_INLINE},
|
{"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"decrby",decrbyCommand,3,REDIS_CMD_INLINE},
|
{"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"getset",getSetCommand,3,REDIS_CMD_BULK},
|
{"getset",getSetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
|
||||||
{"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE},
|
{"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE},
|
||||||
{"select",selectCommand,2,REDIS_CMD_INLINE},
|
{"select",selectCommand,2,REDIS_CMD_INLINE},
|
||||||
{"move",moveCommand,3,REDIS_CMD_INLINE},
|
{"move",moveCommand,3,REDIS_CMD_INLINE},
|
||||||
@ -422,7 +429,7 @@ static struct redisCommand cmdTable[] = {
|
|||||||
{"sync",syncCommand,1,REDIS_CMD_INLINE},
|
{"sync",syncCommand,1,REDIS_CMD_INLINE},
|
||||||
{"flushdb",flushdbCommand,1,REDIS_CMD_INLINE},
|
{"flushdb",flushdbCommand,1,REDIS_CMD_INLINE},
|
||||||
{"flushall",flushallCommand,1,REDIS_CMD_INLINE},
|
{"flushall",flushallCommand,1,REDIS_CMD_INLINE},
|
||||||
{"sort",sortCommand,-2,REDIS_CMD_INLINE},
|
{"sort",sortCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
|
||||||
{"info",infoCommand,1,REDIS_CMD_INLINE},
|
{"info",infoCommand,1,REDIS_CMD_INLINE},
|
||||||
{"monitor",monitorCommand,1,REDIS_CMD_INLINE},
|
{"monitor",monitorCommand,1,REDIS_CMD_INLINE},
|
||||||
{"ttl",ttlCommand,2,REDIS_CMD_INLINE},
|
{"ttl",ttlCommand,2,REDIS_CMD_INLINE},
|
||||||
@ -864,6 +871,7 @@ static void initServerConfig() {
|
|||||||
server.requirepass = NULL;
|
server.requirepass = NULL;
|
||||||
server.shareobjects = 0;
|
server.shareobjects = 0;
|
||||||
server.maxclients = 0;
|
server.maxclients = 0;
|
||||||
|
server.maxmemory = 0;
|
||||||
ResetServerSaveParams();
|
ResetServerSaveParams();
|
||||||
|
|
||||||
appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */
|
appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */
|
||||||
@ -1024,6 +1032,8 @@ static void loadServerConfig(char *filename) {
|
|||||||
}
|
}
|
||||||
} else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
|
} else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
|
||||||
server.maxclients = atoi(argv[1]);
|
server.maxclients = atoi(argv[1]);
|
||||||
|
} else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
|
||||||
|
server.maxmemory = atoi(argv[1]);
|
||||||
} else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
|
} else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
|
||||||
server.masterhost = sdsnew(argv[1]);
|
server.masterhost = sdsnew(argv[1]);
|
||||||
server.masterport = atoi(argv[2]);
|
server.masterport = atoi(argv[2]);
|
||||||
@ -1207,6 +1217,9 @@ static int processCommand(redisClient *c) {
|
|||||||
struct redisCommand *cmd;
|
struct redisCommand *cmd;
|
||||||
long long dirty;
|
long long dirty;
|
||||||
|
|
||||||
|
/* Free some memory if needed (maxmemory setting) */
|
||||||
|
if (server.maxmemory) freeMemoryIfNeeded();
|
||||||
|
|
||||||
/* The QUIT command is handled as a special case. Normal command
|
/* The QUIT command is handled as a special case. Normal command
|
||||||
* procs are unable to close the client connection safely */
|
* procs are unable to close the client connection safely */
|
||||||
if (!strcasecmp(c->argv[0]->ptr,"quit")) {
|
if (!strcasecmp(c->argv[0]->ptr,"quit")) {
|
||||||
@ -1223,6 +1236,10 @@ static int processCommand(redisClient *c) {
|
|||||||
addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n"));
|
addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n"));
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (server.maxmemory && cmd->flags & REDIS_CMD_DENYOOM && zmalloc_used_memory() > server.maxmemory) {
|
||||||
|
addReplySds(c,sdsnew("-ERR command not allowed when used memory > 'maxmemory'\r\n"));
|
||||||
|
resetClient(c);
|
||||||
|
return 1;
|
||||||
} else if (cmd->flags & REDIS_CMD_BULK && c->bulklen == -1) {
|
} else if (cmd->flags & REDIS_CMD_BULK && c->bulklen == -1) {
|
||||||
int bulklen = atoi(c->argv[c->argc-1]->ptr);
|
int bulklen = atoi(c->argv[c->argc-1]->ptr);
|
||||||
|
|
||||||
@ -3974,6 +3991,58 @@ static void slaveofCommand(redisClient *c) {
|
|||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================ Maxmemory directive ======================== */
|
||||||
|
|
||||||
|
/* This function gets called when 'maxmemory' is set on the config file to limit
|
||||||
|
* the max memory used by the server, and we are out of memory.
|
||||||
|
* This function will try to, in order:
|
||||||
|
*
|
||||||
|
* - Free objects from the free list
|
||||||
|
* - Try to remove keys with an EXPIRE set
|
||||||
|
*
|
||||||
|
* It is not possible to free enough memory to reach used-memory < maxmemory
|
||||||
|
* the server will start refusing commands that will enlarge even more the
|
||||||
|
* memory usage.
|
||||||
|
*/
|
||||||
|
static void freeMemoryIfNeeded(void) {
|
||||||
|
while (server.maxmemory && zmalloc_used_memory() > server.maxmemory) {
|
||||||
|
if (listLength(server.objfreelist)) {
|
||||||
|
robj *o;
|
||||||
|
|
||||||
|
listNode *head = listFirst(server.objfreelist);
|
||||||
|
o = listNodeValue(head);
|
||||||
|
listDelNode(server.objfreelist,head);
|
||||||
|
zfree(o);
|
||||||
|
} else {
|
||||||
|
int j, k, freed = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < server.dbnum; j++) {
|
||||||
|
int minttl = -1;
|
||||||
|
robj *minkey = NULL;
|
||||||
|
struct dictEntry *de;
|
||||||
|
|
||||||
|
if (dictSize(server.db[j].expires)) {
|
||||||
|
freed = 1;
|
||||||
|
/* From a sample of three keys drop the one nearest to
|
||||||
|
* the natural expire */
|
||||||
|
for (k = 0; k < 3; k++) {
|
||||||
|
time_t t;
|
||||||
|
|
||||||
|
de = dictGetRandomKey(server.db[j].expires);
|
||||||
|
t = (time_t) dictGetEntryVal(de);
|
||||||
|
if (minttl == -1 || t < minttl) {
|
||||||
|
minkey = dictGetEntryKey(de);
|
||||||
|
minttl = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleteKey(server.db+j,minkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!freed) return; /* nothing to free... */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* =================================== Main! ================================ */
|
/* =================================== Main! ================================ */
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
12
redis.conf
12
redis.conf
@ -88,6 +88,18 @@ databases 16
|
|||||||
|
|
||||||
# maxclients 128
|
# maxclients 128
|
||||||
|
|
||||||
|
# Don't use more memory than the specified amount of bytes.
|
||||||
|
# When the memory limit is reached Redis will try to remove keys with an
|
||||||
|
# EXPIRE set. It will try to start freeing keys that are going to expire
|
||||||
|
# in little time and preserve keys with a longer time to live.
|
||||||
|
# Redis will also try to remove objects from free lists if possible.
|
||||||
|
#
|
||||||
|
# If all this fails, Redis will start to reply with errors to commands
|
||||||
|
# that will use more memory, like SET, LPUSH, and so on, and will continue
|
||||||
|
# to reply to most read-only commands like GET.
|
||||||
|
|
||||||
|
# maxmemory <bytes>
|
||||||
|
|
||||||
############################### ADVANCED CONFIG ###############################
|
############################### ADVANCED CONFIG ###############################
|
||||||
|
|
||||||
# Glue small output buffers together in order to send small replies in a
|
# Glue small output buffers together in order to send small replies in a
|
||||||
|
Loading…
Reference in New Issue
Block a user