Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
165346ca
Commit
165346ca
authored
Oct 14, 2010
by
antirez
Browse files
implemented different algorithms for maxmemory
parent
ef59a8bc
Changes
5
Hide whitespace changes
Inline
Side-by-side
redis.conf
View file @
165346ca
...
@@ -148,6 +148,25 @@ dir ./
...
@@ -148,6 +148,25 @@ dir ./
#
#
# maxmemory <bytes>
# maxmemory <bytes>
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached? You can select among five behavior:
#
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys->random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
#
# maxmemory-policy volatile-lru
# LRU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can select as well the sample
# size to check. For instance for default Redis will check three keys and
# pick the one that was used less recently, you can change the sample size
# using the following configuration directive.
#
# maxmemory-sample 3
############################## APPEND ONLY MODE ###############################
############################## APPEND ONLY MODE ###############################
# By default Redis asynchronously dumps the dataset on disk. If you can live
# By default Redis asynchronously dumps the dataset on disk. If you can live
...
...
src/config.c
View file @
165346ca
...
@@ -123,6 +123,21 @@ void loadServerConfig(char *filename) {
...
@@ -123,6 +123,21 @@ void loadServerConfig(char *filename) {
server
.
maxclients
=
atoi
(
argv
[
1
]);
server
.
maxclients
=
atoi
(
argv
[
1
]);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"maxmemory"
)
&&
argc
==
2
)
{
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"maxmemory"
)
&&
argc
==
2
)
{
server
.
maxmemory
=
memtoll
(
argv
[
1
],
NULL
);
server
.
maxmemory
=
memtoll
(
argv
[
1
],
NULL
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"maxmemory-policy"
)
&&
argc
==
2
)
{
if
(
!
strcasecmp
(
argv
[
1
],
"volatile-lru"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_VOLATILE_LRU
;
}
else
if
(
!
strcasecmp
(
argv
[
1
],
"volatile-random"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_VOLATILE_RANDOM
;
}
else
if
(
!
strcasecmp
(
argv
[
1
],
"volatile-ttl"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_VOLATILE_TTL
;
}
else
if
(
!
strcasecmp
(
argv
[
1
],
"allkeys-lru"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_ALLKEYS_LRU
;
}
else
if
(
!
strcasecmp
(
argv
[
1
],
"allkeys-random"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_ALLKEYS_RANDOM
;
}
else
{
err
=
"Invalid maxmemory policy"
;
goto
loaderr
;
}
}
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
]);
...
@@ -242,6 +257,20 @@ void configSetCommand(redisClient *c) {
...
@@ -242,6 +257,20 @@ void configSetCommand(redisClient *c) {
ll
<
0
)
goto
badfmt
;
ll
<
0
)
goto
badfmt
;
server
.
maxmemory
=
ll
;
server
.
maxmemory
=
ll
;
if
(
server
.
maxmemory
)
freeMemoryIfNeeded
();
if
(
server
.
maxmemory
)
freeMemoryIfNeeded
();
}
else
if
(
!
strcasecmp
(
c
->
argv
[
2
]
->
ptr
,
"maxmemory-policy"
))
{
if
(
!
strcasecmp
(
o
->
ptr
,
"volatile-lru"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_VOLATILE_LRU
;
}
else
if
(
!
strcasecmp
(
o
->
ptr
,
"volatile-random"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_VOLATILE_RANDOM
;
}
else
if
(
!
strcasecmp
(
o
->
ptr
,
"volatile-ttl"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_VOLATILE_TTL
;
}
else
if
(
!
strcasecmp
(
o
->
ptr
,
"allkeys-lru"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_ALLKEYS_LRU
;
}
else
if
(
!
strcasecmp
(
o
->
ptr
,
"allkeys-random"
))
{
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_ALLKEYS_RANDOM
;
}
else
{
goto
badfmt
;
}
}
else
if
(
!
strcasecmp
(
c
->
argv
[
2
]
->
ptr
,
"timeout"
))
{
}
else
if
(
!
strcasecmp
(
c
->
argv
[
2
]
->
ptr
,
"timeout"
))
{
if
(
getLongLongFromObject
(
o
,
&
ll
)
==
REDIS_ERR
||
if
(
getLongLongFromObject
(
o
,
&
ll
)
==
REDIS_ERR
||
ll
<
0
||
ll
>
LONG_MAX
)
goto
badfmt
;
ll
<
0
||
ll
>
LONG_MAX
)
goto
badfmt
;
...
@@ -358,6 +387,21 @@ void configGetCommand(redisClient *c) {
...
@@ -358,6 +387,21 @@ void configGetCommand(redisClient *c) {
addReplyBulkCString
(
c
,
buf
);
addReplyBulkCString
(
c
,
buf
);
matches
++
;
matches
++
;
}
}
if
(
stringmatch
(
pattern
,
"maxmemory-policy"
,
0
))
{
char
*
s
;
switch
(
server
.
maxmemory_policy
)
{
case
REDIS_MAXMEMORY_VOLATILE_LRU
:
s
=
"volatile-lru"
;
break
;
case
REDIS_MAXMEMORY_VOLATILE_TTL
:
s
=
"volatile-ttl"
;
break
;
case
REDIS_MAXMEMORY_VOLATILE_RANDOM
:
s
=
"volatile-random"
;
break
;
case
REDIS_MAXMEMORY_ALLKEYS_LRU
:
s
=
"allkeys-lru"
;
break
;
case
REDIS_MAXMEMORY_ALLKEYS_RANDOM
:
s
=
"allkeys-random"
;
break
;
default:
s
=
"unknown"
;
break
;
/* too harmless to panic */
}
addReplyBulkCString
(
c
,
"maxmemory-policy"
);
addReplyBulkCString
(
c
,
s
);
matches
++
;
}
if
(
stringmatch
(
pattern
,
"timeout"
,
0
))
{
if
(
stringmatch
(
pattern
,
"timeout"
,
0
))
{
char
buf
[
128
];
char
buf
[
128
];
...
...
src/object.c
View file @
165346ca
...
@@ -443,8 +443,9 @@ char *strEncoding(int encoding) {
...
@@ -443,8 +443,9 @@ char *strEncoding(int encoding) {
* requested, using an approximated LRU algorithm. */
* requested, using an approximated LRU algorithm. */
unsigned
long
estimateObjectIdleTime
(
robj
*
o
)
{
unsigned
long
estimateObjectIdleTime
(
robj
*
o
)
{
if
(
server
.
lruclock
>=
o
->
lru
)
{
if
(
server
.
lruclock
>=
o
->
lru
)
{
return
(
server
.
lruclock
-
o
->
lru
)
*
60
;
return
(
server
.
lruclock
-
o
->
lru
)
*
REDIS_LRU_CLOCK_RESOLUTION
;
}
else
{
}
else
{
return
((
REDIS_LRU_CLOCK_MAX
-
o
->
lru
)
+
server
.
lruclock
)
*
60
;
return
((
REDIS_LRU_CLOCK_MAX
-
o
->
lru
)
+
server
.
lruclock
)
*
REDIS_LRU_CLOCK_RESOLUTION
;
}
}
}
}
src/redis.c
View file @
165346ca
...
@@ -478,6 +478,10 @@ void activeExpireCycle(void) {
...
@@ -478,6 +478,10 @@ void activeExpireCycle(void) {
}
}
}
}
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
serverCron
(
struct
aeEventLoop
*
eventLoop
,
long
long
id
,
void
*
clientData
)
{
int
j
,
loops
=
server
.
cronloops
++
;
int
j
,
loops
=
server
.
cronloops
++
;
...
@@ -491,14 +495,18 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -491,14 +495,18 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
* To access a global var is faster than calling time(NULL) */
* To access a global var is faster than calling time(NULL) */
server
.
unixtime
=
time
(
NULL
);
server
.
unixtime
=
time
(
NULL
);
/* We have just 22 bits per object for LRU information.
/* We have just 22 bits per object for LRU information.
* So we use an (eventually wrapping) LRU clock with
minute
s resolution.
* So we use an (eventually wrapping) LRU clock with
10 second
s resolution.
* 2^22
minute
s
a
re
more than 7
years.
* 2^22
bits with 10 second
s re
soluton is more or less 1.5
years.
*
*
* Note that even if this will wrap after
7
years it's not a problem,
* 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
* everything will still work but just some object will appear younger
* to Redis :)
* to Redis. But for this to happen a given object should never be touched
* for 1.5 years.
*
* Note that you can change the resolution altering the
* REDIS_LRU_CLOCK_RESOLUTION define.
*/
*/
server
.
lruclock
=
(
time
(
NULL
)
/
60
)
&
REDIS_LRU_CLOCK_MAX
;
updateLRUClock
()
;
/* We received a SIGTERM, shutting down here in a safe way, as it is
/* We received a SIGTERM, shutting down here in a safe way, as it is
* not ok doing so inside the signal handler. */
* not ok doing so inside the signal handler. */
...
@@ -729,6 +737,8 @@ void initServerConfig() {
...
@@ -729,6 +737,8 @@ void initServerConfig() {
server
.
maxclients
=
0
;
server
.
maxclients
=
0
;
server
.
blpop_blocked_clients
=
0
;
server
.
blpop_blocked_clients
=
0
;
server
.
maxmemory
=
0
;
server
.
maxmemory
=
0
;
server
.
maxmemory_policy
=
REDIS_MAXMEMORY_VOLATILE_LRU
;
server
.
maxmemory_samples
=
3
;
server
.
vm_enabled
=
0
;
server
.
vm_enabled
=
0
;
server
.
vm_swap_file
=
zstrdup
(
"/tmp/redis-%p.vm"
);
server
.
vm_swap_file
=
zstrdup
(
"/tmp/redis-%p.vm"
);
server
.
vm_page_size
=
256
;
/* 256 bytes per page */
server
.
vm_page_size
=
256
;
/* 256 bytes per page */
...
@@ -742,6 +752,7 @@ void initServerConfig() {
...
@@ -742,6 +752,7 @@ void initServerConfig() {
server
.
list_max_ziplist_value
=
REDIS_LIST_MAX_ZIPLIST_VALUE
;
server
.
list_max_ziplist_value
=
REDIS_LIST_MAX_ZIPLIST_VALUE
;
server
.
set_max_intset_entries
=
REDIS_SET_MAX_INTSET_ENTRIES
;
server
.
set_max_intset_entries
=
REDIS_SET_MAX_INTSET_ENTRIES
;
server
.
shutdown_asap
=
0
;
server
.
shutdown_asap
=
0
;
updateLRUClock
();
resetServerSaveParams
();
resetServerSaveParams
();
...
@@ -1327,10 +1338,93 @@ int tryFreeOneObjectFromFreelist(void) {
...
@@ -1327,10 +1338,93 @@ int tryFreeOneObjectFromFreelist(void) {
* memory usage.
* memory usage.
*/
*/
void
freeMemoryIfNeeded
(
void
)
{
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
)
{
while
(
server
.
maxmemory
&&
zmalloc_used_memory
()
>
server
.
maxmemory
)
{
int
j
,
k
,
freed
=
0
;
int
j
,
k
,
freed
=
0
;
/* Basic strategy -- remove objects from the free list. */
if
(
tryFreeOneObjectFromFreelist
()
==
REDIS_OK
)
continue
;
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
++
)
{
for
(
j
=
0
;
j
<
server
.
dbnum
;
j
++
)
{
int
minttl
=
-
1
;
int
minttl
=
-
1
;
sds
minkey
=
NULL
;
sds
minkey
=
NULL
;
...
...
src/redis.h
View file @
165346ca
...
@@ -198,6 +198,13 @@
...
@@ -198,6 +198,13 @@
#define REDIS_OP_DIFF 1
#define REDIS_OP_DIFF 1
#define REDIS_OP_INTER 2
#define REDIS_OP_INTER 2
/* Redis maxmemory strategies */
#define REDIS_MAXMEMORY_VOLATILE_LRU 0
#define REDIS_MAXMEMORY_VOLATILE_TTL 1
#define REDIS_MAXMEMORY_VOLATILE_RANDOM 2
#define REDIS_MAXMEMORY_ALLKEYS_LRU 3
#define REDIS_MAXMEMORY_ALLKEYS_RANDOM 4
/* We can print the stacktrace, so our assert is defined this way: */
/* We can print the stacktrace, so our assert is defined this way: */
#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
#define redisPanic(_e) _redisPanic(#_e,__FILE__,__LINE__),_exit(1)
#define redisPanic(_e) _redisPanic(#_e,__FILE__,__LINE__),_exit(1)
...
@@ -212,6 +219,7 @@ void _redisPanic(char *msg, char *file, int line);
...
@@ -212,6 +219,7 @@ void _redisPanic(char *msg, char *file, int line);
/* The actual Redis Object */
/* The actual Redis Object */
#define REDIS_LRU_CLOCK_MAX ((1<<21)-1)
/* Max value of obj->lru */
#define REDIS_LRU_CLOCK_MAX ((1<<21)-1)
/* Max value of obj->lru */
#define REDIS_LRU_CLOCK_RESOLUTION 10
/* LRU clock resolution in seconds */
typedef
struct
redisObject
{
typedef
struct
redisObject
{
unsigned
type
:
4
;
unsigned
type
:
4
;
unsigned
storage
:
2
;
/* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
unsigned
storage
:
2
;
/* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
...
@@ -391,6 +399,8 @@ struct redisServer {
...
@@ -391,6 +399,8 @@ struct redisServer {
int
replstate
;
int
replstate
;
unsigned
int
maxclients
;
unsigned
int
maxclients
;
unsigned
long
long
maxmemory
;
unsigned
long
long
maxmemory
;
int
maxmemory_policy
;
int
maxmemory_samples
;
unsigned
int
blpop_blocked_clients
;
unsigned
int
blpop_blocked_clients
;
unsigned
int
vm_blocked_clients
;
unsigned
int
vm_blocked_clients
;
/* Sort parameters - qsort_r() is only available under BSD so we
/* Sort parameters - qsort_r() is only available under BSD so we
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment