Redis过期--淘汰机制的解析和内存占用过高的解决方案
echo编辑整理,欢迎转载,转载请声明文章来源。欢迎添加echo微信(微信号:t2421499075)交流学习。 百战不败,依不自称常胜,百败不颓,依能奋力前行。——这才是真正的堪称强大!!!
Redis在我们平时的开发或者练习的时候,往往很容易忽略一个问题,那就是我们的Redis内存占满的问题。但是在真是的商业开发中,Redis的实际占满是真正会存在这样的问题的。那么如果Redis在某一刻占满内存,我们又没有对它进行相应的设置它会出现什么情况呢?会不会导致我们整个因为使用Redis而整个业务垮掉?这就是我们本篇文章所要讲述的问题。
什么是Redis淘汰机制
Redis内存淘汰机制其实简单讲就是将过期的数据或者很久没有访问,或者在一段时间内很少有访问的数据进行删除。它分为很多中,有主动的数据淘汰,如:用户设定过期时间。有被动的淘汰,比如:Redis数据占满了内存,这个时候就会将过期的数据或者很久没有访问的数据删除掉。
Redis的淘汰有哪些类型
- 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
- 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
- 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
定时过期的问题:缓存雪崩
很多人可能对这个词很熟悉,因为有很多的商业案例展示了血淋淋的教训,但是也有一部分人估计没有接触过。缓存雪崩其实也不是什么新词,它主要的引起原因就是指缓存中数据大批量的到过期时间。定时过期它本身就有一个缺点,那就是会占用大量的CPU资源,如果我们主动设置过期时间的键过多,在同一时间过期,很有可能就会造就我们Redis挂掉。但是这并不是最可怕的,雪崩不仅仅影响自己,还在我们的业务中影响数据库。因为我们很多业务设计都是在我们Redis的数据过期之后,从新查询数据库,但我们Redis主动批量过期的时候,会有大量的请求发送到我们的数据库,很有可能导致我们的数据库也挂掉。这才是最大的问题所在。
解决方案:
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
- 设置热点数据永远不过期。
从几种淘汰策略中其实我们可以看到基本的一些问题所在,所以我们在使用缓存的时候最好有一个全面的了解和全面的考虑应对。在实际开发中,我们更应该多去关注的和了解的是定期过期,因为它涉及真实开发中的一些问题。所以我们应该提前设置好。
怎么设置定期过期最大使用内存
定期过期的最大内存设置在我们的redis.conf文件中,我们可以在该文件中看到这个配置:maxmemory <bytes>,但是这个配置一般都是注释掉的,也就是说安装之后如果我们没有主动对他进行配置,那么他就不会有默认大小值。对应的它不设置的情况下,那么它可以使用多少的内存空间呢?这个跟系统有关。如果说我们将Redis安装在32位的系统上,它的最大使用内存空间应该是在3G左右,如果是64位的系统,那么可以将我们的内存占满。当然如果真正占满内存,这是一件比较恶劣的事情,不仅仅访问Redis的时候,我们不能在进行写的操作,而且我们系统本身的其他操作也会受到限制。所以我们可以采用命令来对它进行一个初始化的设置
config set maxmemory 268435456
使用命令进行设置之后我们需要重启Redis才能生效。当然我们也可以直接找到Redis的安装目录,然后使用vi命令,直接更改配置文件中的对应的该内容,更改完之后,重启即可。
定期过期的淘汰策略
- volatile-lru:根据LRU算法生成的过期时间来删除。
- allkeys-lru:根据LRU算法删除任何key。
- volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
- allkeys-lfu:从所有键中驱逐使用频率最少的键
- volatile-random:根据过期设置来随机删除key。
- allkeys-random:无差别随机删。
- volatile-ttl:根据最近过期时间来删除(辅以TTL)
- noeviction:谁也不删,直接在写操作时返回错误。
随机淘汰策略
随机找hash桶再次hash指定位置的dictEntry即可。就是在场景REDIS_MAXMEMORY_VOLATILE_RANDOM和REDIS_MAXMEMORY_ALLKEYS_LRU情况下的待淘汰的key。我们可以一观它的源码:
dictEntry *dictGetRandomKey(dict *d)
{
dictEntry *he, *orighe;
unsigned int h;
int listlen, listele;
if (dictSize(d) == 0) return NULL;
if (dictIsRehashing(d)) _dictRehashStep(d);
if (dictIsRehashing(d)) {
// T = O(N)
do {
h = random() % (d->ht[0].size+d->ht[1].size);
he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
d->ht[0].table[h];
} while(he == NULL);
} else {
// T = O(N)
do {
h = random() & d->ht[0].sizemask;
he = d->ht[0].table[h];
} while(he == NULL);
}
/* Now we found a non empty bucket, but it is a linked
* list and we need to get a random element from the list.
* The only sane way to do so is counting the elements and
* select a random index. */
listlen = 0;
orighe = he;
while(he) {
he = he->next;
listlen++;
}
listele = random() % listlen;
he = orighe;
// T = O(1)
while(listele--) he = he->next;
return he;
}
TTL时间淘汰
for (k = 0; k < server.maxmemory_samples; k++) {
sds thiskey;
long thisval;
de = dictGetRandomKey(dict);
thiskey = dictGetKey(de);
thisval = (long) dictGetVal(de);
/* Expire sooner (minor expire unix timestamp) is better
* candidate for deletion */
if (bestkey == NULL || thisval < bestval) {
bestkey = thiskey;
bestval = thisval;
}
}
更多源码,请参考redis官网。这里只是简单的展示两种。我们可以根据这样的代码来对我们的redis的定期过期做一个合理的配置
思考:
基于一个数据结构做缓存,怎么实现一个LRU算法?
做一个有底线的博客主
Redis过期--淘汰机制的解析和内存占用过高的解决方案的更多相关文章
- Redis系列--内存淘汰机制(含单机版内存优化建议)
https://blog.csdn.net/Jack__Frost/article/details/72478400?locationNum=13&fps=1 每台redis的服务器的内存都是 ...
- [转帖]Linux中buff/cache内存占用过高解决办法
Linux中buff/cache内存占用过高解决办法 https://www.cnblogs.com/rocky-AGE-24/p/7629500.html /proc/sys/vm/drop_cac ...
- CLR Profile解决内存占用过高
CLR Profile解决内存占用过高的问题 炮哥:"嘿,哥们,忙啥呢,电脑卡成这逼样." 勇哥:"在用CLR Profile工具分析下FlexiPrint的内存占用情况 ...
- [2017-08-09]一则使用WinDbg工具调试iis进程调查内存占用过高的案例
最近遇到一个奇葩内存问题,跟了三四天,把Windbg玩熟了,所以打算分享下. 症状简介 我们团队的DEV开发环境只有一台4核16G的win2012r2. 这台服务器上装了SqlServer.TFS(项 ...
- 通过修改my.ini配置文件来解决MySQL 5.6 内存占用过高的问题
打开后台进程发现mysql占用的内存达到400+M. 修改一下my.ini这个配置文件的配置选项是可以限制MySQL5.6内存占用过高这一问题的,具体修改选项如下: performance_schem ...
- 【转】一则使用WinDbg工具调试iis进程调查内存占用过高的案例
最近遇到一个奇葩内存问题,跟了三四天,把Windbg玩熟了,所以打算分享下. 症状简介 我们团队的DEV开发环境只有一台4核16G的win2012r2.这台服务器上装了SqlServer.TFS(项目 ...
- Spring cloud开发内存占用过高解决方法
https://blog.csdn.net/wanhuiguizong/article/details/79289986 版权声明:本文为博主原创文章,转载请声明文章来源和原文链接. https:// ...
- (转)aix非计算内存 占用过高 案例一则
原文:http://www.talkwithtrend.com/Article/28621 两台小型机组成的RAC环境,在用topas查看资源使用情况时,发现一台机器的非计算内存占用过高: MEMOR ...
- Window下MySql 5.6 安装后内存占用很高的问题
Window下MySql 5.6 安装后内存占用很高的问题 刚刚准备玩一把mysql,初学者 环境是window 7和window sever 2008, mysql是最新的5.6, 发现的问题是安装 ...
随机推荐
- Laravel Entrust 权限管理扩展包的使用笔记
简介 Entrust 是一个简洁而灵活的基于角色进行权限管理的 Laravel 扩展包.针对 Laravel 5,官方推荐的安装版本是 5.2.x-dev.它的详细使用方法请查看 Entrust Gi ...
- 【Java】后台将文件上传至远程服务器
问题:由于系统在局域网(能访问外网)内,但外网无法请求局域网内服务器文件和进行处理文件. 解决:建立文件服务器,用于存储文件及外网调用. 客户端(文件上传): package cn.hkwl.lm.u ...
- C# WinForm 跨线程访问控件(实用简洁写法)
C# WinForm 跨线程访问控件(实用简洁写法) 1.<C# WinForm 跨线程访问控件(实用简洁写法)> 2.<基于.NET环境,C#语言 实现 TCP NAT ...
- [以太坊源代码分析] I.区块和交易,合约和虚拟机
最近在看以太坊(Ethereum)的源代码, 初初看出点眉目. 区块链是近年热点之一,面向大众读者介绍概念的文章无数,有兴趣的朋友可自行搜索.我会从源代码实现入手,较系统的介绍一下以太坊的系统设计和协 ...
- Timed out after 30000 ms while waiting to connect
今天使用mongo-java-drive写连接mongo的客户端,着实被上面那个错坑了一把.回顾一下解决过程: 报错: com.mongodb.MongoTimeoutException: Timed ...
- CSS3属性—— line-clamp控制文本行数
说明: 限制在一个块元素显示的文本的行数. -webkit-line-clamp 是一个 不规范的属性(unsupported WebKit property),它没有出现在 CSS 规范草案中. 为 ...
- wampserver 运行橙色,80端口没有被占用,查看错误日志方法
wampserver运行时橙色,经检查80端口并没有被占用,试了很多种方法都无效,去查看错误日志吧 1.以管理员身份打开CMD 注意这里必须是管理员身份的CMD ,powershell不行的 进入wa ...
- nm 命令能够显示目标文件中重载函数的名字改变(C++)
#include <stdio.h> #include <iostream> using std::cout; using std::endl; //这里的两个不同的add函数 ...
- RF中的if判断
关键字run keyword if 格式如下: Run Keyword If 判断条件 其他关键字 ... ELSE IF 判断条件 其他关键字 ... ELSE ...
- 如何让excel文件读取变得更简单
今天给大家安利一款excel文件导入神器,easyexcel,官方地址:(https://github.com/alibaba/easyexcel). 在官网文档中有介绍了其性能. 从上面的性能测试可 ...