本文及后续文章,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. win 10 dpi:150% 与 win 7 dpi:150% 的不同之处

    由于 win 7 和 win 10 的 dpi 处理方式不同,导致我们写的客户端程序在 win 7 上运行正常,在 win 10(dpi:150%)上运行不正常了. 具体的描述,可参考:解决win10 ...

  2. 记录下扣jio的2018年

    踩着18年的尾巴,写下这篇总结,既给18年画上句号,也展望19年,制定下计划. 自17年底正式接手团队项目管理工作以来,虽然前面一年都干了大部分工作,但正式走到这个位置上来,还是有一部分的期待.接手之 ...

  3. JMeter二次开发环境配置

    本文主要介绍如何在Eclipse中配置JMeter开发环境. 一.下载JMeter源码 1.在JMeter官网下载二进制包和源码包: 解压备用: 二进制解压后文件夹名称为“jmeter_release ...

  4. crm 一级菜单排序,二级菜单选中并且展开,非菜单权限的归属,权限粒度控制到按钮级别

    排序 /rbac/templatetags/rbac.py from django import template from django.conf import settings import re ...

  5. [面试]Actor模型

    Actor模型 面试中自己说话不利落, 或者自己对知识点认识不全面.在这里进行一下记录. 理论部分都是收集(copy)自网上其他的博客. 什么是Actor模型 参与者模式(英语:Actor model ...

  6. mockplus 原型设计工具

    国产原型工具 http://www.mockplus.cn, 该工具功能很棒. 每次打开软件都需先登陆, 好在项目文件是可以保存到本地, 可以注册为免费版/个人版/团队版/企业版. 我是免费账号, 功 ...

  7. IDEA中debug启动tomcat报错。Error running t8:Unable to open debugger port(127.0.0.1:49225):java.net.BindException"Address alread in use:JVM_Bind"

    解决办法: 1,如下图打开项目配置的tomcat的“Edit Configurations...” 2,打开“Startup/Connection”--------"Debug"- ...

  8. PhpStorm常用快捷键以及如何连接外部服务器

    PhpStorm常用快捷键以及如何连接外部服务器 PhpStorm作为我们phper使用的一款IDE,其功能是非常强大的,现在记录下常用的快捷键以及如何使用它与外部服务器进行连接使用. 一.Keyma ...

  9. 安卓触控一体机的逆袭之路_追逐品质_支持APP软件安卓

    显示性能参数 接口:RGB信号 分辨率:1024*600 比例16:9 显示尺寸(A.A.):222.72*(W)*125.28(H)mm 外围尺寸:235.0(W)*143.0(H)*4.5(T)m ...

  10. Sping 里面的适配器模式的实现

    适配器模式----------设计模式最近在看SpringMVC源码,从中看到了比较优秀的设计模式所以来分享下. 1.适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口,Adap ...