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, 发现的问题是安装 ...
随机推荐
- 访问http接口时返回502 Bad Getway什么原因怎么解决
使用 httpclient 工具通过代理服务器请求第三方http 接口,多次返回 502 Bad Getway,少数返回正常. 502 Bad Getway是什么意思? 502 Bad Gateway ...
- 【线性表基础】顺序表和单链表的插入、删除等基本操作【Java版】
本文表述了线性表及其基本操作的代码[Java实现] 参考书籍 :<数据结构 --Java语言描述>/刘小晶 ,杜选主编 线性表需要的基本功能有:动态地增长或收缩:对线性表的任何数据元素进行 ...
- ionic3 浏览器端返回
首屏component.ts文件中使用setupBrowserBackButtonBehavior() { // Register browser back button action(s) wind ...
- 【TencentOS tiny】深度源码分析(3)——队列
队列基本概念 队列是一种常用于任务间通信的数据结构,队列可以在任务与任务间.中断和任务间传递消息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息,当队列中的消息是空时, ...
- UVa12105 越大越好
题文:https://vjudge.net/problem/12364(或者见紫书) 题解: 因为题目中有两个限制条件,那么我们就顺着题目的意思来dp,设dp[i][j]表示目前还剩下的i个火柴,用这 ...
- Qt+VC2010+glew环境安装配置
Qt的源码及预编译安装包在 Qt Archive下载,http://download.qt.io/archive/qt/, 目前最新的是Qt5,其中和Qt4不同的是,Qt5多了个QOpenGLWidg ...
- HTML5 lufylegend引擎学习(一) -- 剪刀石头布小游戏
网址:http://www.lufylegend.com/ <!DOCTYPE html> <html> <head> <title>A Little ...
- Cocos2d-x 学习笔记(15.4) EventDispatcher 事件分发具体逻辑 dispatchEventToListeners函数
dispatchEvent(Event* event)方法在对事件对应的监听器进行重新排序后,进行事件分发操作.具体操作由dispatchEventToListeners方法执行. 该方法声明: vo ...
- opencv::处理边缘
卷积边界问题 图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有完全跟kernel重叠,所以当3x3滤波时候有1个像素的边缘没有被处理,5x5滤波的时候有2个像素的边缘没有被处理. 处理边缘 ...
- boost::multi_index 多索引容器
#include "stdafx.h" #include <string> #include <boost/multi_index_container.hpp&g ...