本文及后续文章,Redis版本均是v3.2.8

一、内存回收策略

maxmemory配置用于配置Redis存储数据时指定限制的内存大小。我们可以通过redis.conf配置或者使用CONFIG SET命令来进行运行时配置。

例如在redis.conf文件中,配置内存限制为100mb

maxmemory 100mb

设置maxmemory为0代表没有内存限制。对于64位的系统这是个默认值,对于32位的系统默认内存限制为3GB。

当目前使用的内存超过了设置的最大内存,就要进行内存释放了, 当需要进行内存释放的时候,需要用某种策略对保存的的对象进行删除。

redis中当内存超过限制时,按照配置的策略,淘汰掉相应的key-value,使得内存可以继续留有足够的空间保存新的数据。redis 在确定了要驱逐某个键值对后,会删除这个数据,并将这个数据变更消息发布到本地(AOF 持久化)和从机(主从连接)。

当maxmemory限制达到的时候,Redis采取内存回收的策略由Redis的maxmemory-policy配置来进行决定的。有六种策略:

  • volatile-lru:默认的策略,尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。

  • volatile-random:回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。

  • volatile-ttl:回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。

  • allkeys-lru:尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。

  • allkeys-random:回收随机的键使得新添加的数据有空间存放。

  • no-enviction:禁止淘汰数据

如果没有键满足回收的前提条件的话,策略volatile-lru, volatile-random,volatile-ttl就和no-eviction 差不多了。

选择正确的回收策略是非常重要的,这取决于你的应用的访问模式,不过你可以在运行时进行相关的策略调整,并且监控缓存命中率和没命中的次数,通过Redis INFO命令输出以便调优。

一般的经验规则:

  • 使用allkeys-lru策略:当你希望你的请求符合一个幂定律分布,也就是说,你希望部分的子集元素将比其它其它元素被访问的更多。

  • 使用allkeys-random:如果你是循环访问,所有的键被连续的扫描,或者你希望请求分布正常(所有元素被访问的概率都差不多)。

  • 使用volatile-ttl:如果你想要通过创建缓存对象时设置TTL值,来决定哪些对象应该被过期。

  • allkeys-lru 和 volatile-random策略:当你想要单一的实例实现缓存及持久化一些键时很有用。不过一般运行两个实例是解决这个问题的更好方法。

为键设置过期时间也是需要消耗内存的,所以使用allkeys-lru这种策略更加高效,因为没有必要为键取设置过期时间当内存有压力时。

除此之外还有一个配置项,就是maxmemory-samples,默认值是3,因为上面的策略代码实现的都是近似算法,所以不管是lru算法,还是ttl,都并不是在数据库中所有的数据为基础的算法,因为当数据库的数据很多的时候,这样效率太低,所以代码中都是基于maxmemory-samples个数据的近似算法。

近似LRU的算法,通过对少量keys进行取样,然后回收其中一个最被访问时间较早的key。我们可以通过调整每次回收时检查的采样数量,以实现调整算法的精度。Redis为什么不使用真实的LRU实现是因为这需要太多的内存。不过近似的LRU算法对于应用而言应该是等价的。

最后,我们看下命令在执行前,Redis server做了什么事情?

/* If this function gets called we already read a whole

* command, arguments are in the client argv/argc fields.

* processCommand() execute the command or prepare the

* server for a bulk read from the client.

*

* If C_OK is returned the client is still alive and valid and

* other operations can be performed by the caller. Otherwise

* if C_ERR is returned the client was destroyed (i.e. after QUIT). */

int processCommand(client *c) {

/* The QUIT command is handled separately. Normal command procs will

* go through checking for replication and QUIT will cause trouble

* when FORCE_REPLICATION is enabled and would be implemented in

* a regular command proc. */

if (!strcasecmp(c->argv[0]->ptr,"quit")) {

addReply(c,shared.ok);

c->flags |= CLIENT_CLOSE_AFTER_REPLY;

return C_ERR;

}

/* Now lookup the command and check ASAP about trivial error conditions

* such as wrong arity, bad command name and so forth. */

c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);

if (!c->cmd) {

flagTransaction(c);

addReplyErrorFormat(c,"unknown command '%s'",

(char*)c->argv[0]->ptr);

return C_OK;

} else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||

(c->argc < -c->cmd->arity)) {

flagTransaction(c);

addReplyErrorFormat(c,"wrong number of arguments for '%s' command",

c->cmd->name);

return C_OK;

}

/* Check if the user is authenticated */

if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)

{

flagTransaction(c);

addReply(c,shared.noautherr);

return C_OK;

}

/* If cluster is enabled perform the cluster redirection here.

* However we don't perform the redirection if:

* 1) The sender of this command is our master.

* 2) The command has no key arguments. */

if (server.cluster_enabled &&

!(c->flags & CLIENT_MASTER) &&

!(c->flags & CLIENT_LUA &&

server.lua_caller->flags & CLIENT_MASTER) &&

!(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0 &&

c->cmd->proc != execCommand))

{

int hashslot;

int error_code;

clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,

&hashslot,&error_code);

if (n == NULL || n != server.cluster->myself) {

if (c->cmd->proc == execCommand) {

discardTransaction(c);

} else {

flagTransaction(c);

}

clusterRedirectClient(c,n,hashslot,error_code);

return C_OK;

}

}

/* Handle the maxmemory directive.

*

* First we try to free some memory if possible (if there are volatile

* keys in the dataset). If there are not the only thing we can do

* is returning an error. */

if (server.maxmemory) {

int retval = freeMemoryIfNeeded();

/* freeMemoryIfNeeded may flush slave output buffers. This may result

* into a slave, that may be the active client, to be freed. */

if (server.current_client == NULL) return C_ERR;

/* It was impossible to free enough memory, and the command the client

* is trying to execute is denied during OOM conditions? Error. */

if ((c->cmd->flags & CMD_DENYOOM) && retval == C_ERR) {

flagTransaction(c);

addReply(c, shared.oomerr);

return C_OK;

}

}

/* Don't accept write commands if there are problems persisting on disk

* and if this is a master instance. */

if (((server.stop_writes_on_bgsave_err &&

server.saveparamslen > 0 &&

server.lastbgsave_status == C_ERR) ||

server.aof_last_write_status == C_ERR) &&

server.masterhost == NULL &&

(c->cmd->flags & CMD_WRITE ||

c->cmd->proc == pingCommand))

{

flagTransaction(c);

if (server.aof_last_write_status == C_OK)

addReply(c, shared.bgsaveerr);

else

addReplySds(c,

sdscatprintf(sdsempty(),

"-MISCONF Errors writing to the AOF file: %s\r\n",

strerror(server.aof_last_write_errno)));

return C_OK;

}

/* Don't accept write commands if there are not enough good slaves and

* user configured the min-slaves-to-write option. */

if (server.masterhost == NULL &&

server.repl_min_slaves_to_write &&

server.repl_min_slaves_max_lag &&

c->cmd->flags & CMD_WRITE &&

server.repl_good_slaves_count < server.repl_min_slaves_to_write)

{

flagTransaction(c);

addReply(c, shared.noreplicaserr);

return C_OK;

}

/* Don't accept write commands if this is a read only slave. But

* accept write commands if this is our master. */

if (server.masterhost && server.repl_slave_ro &&

!(c->flags & CLIENT_MASTER) &&

c->cmd->flags & CMD_WRITE)

{

addReply(c, shared.roslaveerr);

return C_OK;

}

/* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */

if (c->flags & CLIENT_PUBSUB &&

c->cmd->proc != pingCommand &&

c->cmd->proc != subscribeCommand &&

c->cmd->proc != unsubscribeCommand &&

c->cmd->proc != psubscribeCommand &&

c->cmd->proc != punsubscribeCommand) {

addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context");

return C_OK;

}

/* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and

* we are a slave with a broken link with master. */

if (server.masterhost && server.repl_state != REPL_STATE_CONNECTED &&

server.repl_serve_stale_data == 0 &&

!(c->cmd->flags & CMD_STALE))

{

flagTransaction(c);

addReply(c, shared.masterdownerr);

return C_OK;

}

/* Loading DB? Return an error if the command has not the

* CMD_LOADING flag. */

if (server.loading && !(c->cmd->flags & CMD_LOADING)) {

addReply(c, shared.loadingerr);

return C_OK;

}

/* Lua script too slow? Only allow a limited number of commands. */

if (server.lua_timedout &&

c->cmd->proc != authCommand &&

c->cmd->proc != replconfCommand &&

!(c->cmd->proc == shutdownCommand &&

c->argc == 2 &&

tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&

!(c->cmd->proc == scriptCommand &&

c->argc == 2 &&

tolower(((char*)c->argv[1]->ptr)[0]) == 'k'))

{

flagTransaction(c);

addReply(c, shared.slowscripterr);

return C_OK;

}

/* Exec the command */

if (c->flags & CLIENT_MULTI &&

c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&

c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)

{

queueMultiCommand(c);

addReply(c,shared.queued);

} else {

call(c,CMD_CALL_FULL);

c->woff = server.master_repl_offset;

if (listLength(server.ready_keys))

handleClientsBlockedOnLists();

}

return C_OK;

}

未完待续

Redis 缓存失效和回收机制的更多相关文章

  1. Redis 缓存失效和回收机制续

    二.Redis Key失效机制 Redis的Key失效机制,主要借助借助EXPIRE命令: EXPIRE key 30 上面的命令即为key设置30秒的过期时间,超过这个时间,我们应该就访问不到这个值 ...

  2. Redis 缓存失效机制

    Redis缓存失效的故事要从EXPIRE这个命令说起,EXPIRE允许用户为某个key指定超时时间,当超过这个时间之后key对应的值会被清除,这篇文章主要在分析Redis源码的基础上站在Redis设计 ...

  3. [转]高并发访问下避免对象缓存失效引发Dogpile效应

    避免Redis/Memcached缓存失效引发Dogpile效应 Redis/Memcached高并发访问下的缓存失效时可能产生Dogpile效应(Cache Stampede效应). 推荐阅读:高并 ...

  4. Redis缓存如何保证一致性

    为什么使用Redis做缓存 MySQL缺点 单机连接数目有限 对数据进行写速度慢 Redis优点 内存操作数据速度快 IO复用,速度快 单线程模型,避免线程切换带来的开销,速度快 一致性问题 读数据的 ...

  5. 深入理解Redis中的主键失效及其实现机制

    参考:http://blog.sina.com.cn/s/articlelist_1221155353_0_1.html 作为一种定期清理无效数据的重要机制,主键失效存在于大多数缓存系统中,Reids ...

  6. redis和memcache缓存击穿,缓存失效问题

    我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 一.缓存穿透 Paste_Image.png Paste_Image.png ...

  7. redis的缓存穿透 缓存并发 缓存失效

    我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 一.缓存穿透 Paste_Image.png Paste_Image.png ...

  8. [技术博客] 用户验证码验证机制---redis缓存数据库的使用

    目录 问题引入 初识redis 实际应用 作者:马振亚 问题引入 在这次的开发过程中,我们的需求中有一个是普通用户可以通过特定的机制申请成为社长.因为只有部分人才能验证成功,所以这个最开始想了两种思路 ...

  9. 缓存机制总结(JVM内置缓存机制,MyBatis和Hibernate缓存机制,Redis缓存)

    一.JVM内置缓存(值存放在JVM缓存中) 我们可以先了解一下Cookie,Session,和Cache Cookie:当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cooki ...

随机推荐

  1. python学习笔记之元祖

    #元祖 只读列表,可循环查询,可切片.#儿子不能改,孙子可能可以改. tu = (1,2,3,'alex',[2,3,4,'taibai'],'egon') print(tu[3]) print(tu ...

  2. jsp:forward动作功能

    jsp:forward动作:引导请求者进入新的页面 例子:login.jsp <center><p>用户登录 </p> <form name="fo ...

  3. Kubernetes之dashboard

    部署dashboard $ wget https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommen ...

  4. 深悉正则(pcre)最大回溯/递归限制

    对于如下的正则 /<script>.*?<\/script>/is 当要匹配的字符串长度大于100014的时候, 就不会得出正确结果: $reg = "/<sc ...

  5. 安装部署 Goaccess

    CentOS 7安装 Goaccess 安装依赖 # yum install ncurses-devel geoip-devel libmaxminddb-devel tokyocabinet-dev ...

  6. C#多线程处理

    创建多线程,并带参数! using System; using System.Collections; using System.Collections.Generic; using System.I ...

  7. windows 系统后台运行 jar 包

    windows平台下 后台运行 jar 包 1.cmd 下执行方式:后台运行  start /min java -server -Xms1024m -Xmx20480m -jar $JAR_NAME. ...

  8. Linux 文件格式转码工具

    Linux 系统下文件编码转换格式工具 ICONV 下载 https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz 源码安装: $ ./con ...

  9. 查看提交历史(git log)

    git log 命令 在完成了几次提交,或者克隆了一个已有提交历史的仓库后,要查看历史提交记录,可以通过git log命令来实现. $ git log commit 0becea8e1966df258 ...

  10. git知识总结-1.git基础之git reference

    1.前言 Git版本管理的内容,是一系列由40bytes SHA-1字符串所代表patch,而Git References,则是对这些字符串的引用(也可以理解为别名).为什么需要别名呢?因为这些字符串 ...