Redis++:Redis 内存爆满 之 淘汰策略
前言:
我们的redis使用的是内存空间来存储数据的,但是内存空间毕竟有限,随着我们存储数据的不断增长,当超过了我们的内存大小时,即在redis中设置的缓存大小(maxmeory 4GB),redis会怎么处理呢?
Redis内存淘汰策略,是被很多小伙伴忽略的知识盲区,注意,是盲区。
注意,Redis如果内存淘汰策略配置不合理,可能会导致Redis无法服务。
今天就来聊聊redis的缓存淘汰策略:↓ ↓ ↓
首先,介绍一下Redis过期删除策略,然后,再介绍Redis淘汰策略.
1):Redis过期删除策略
Redis对于过期的key,有三种删除策略:
- 定期删除(主动删除:由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期主动淘汰一批已过期的key)
- 惰性删除(被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key)
- 当前已用内存超过maxmemory限定时,触发主动清理策略
定期删除:
redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。
Redis 默认会每秒进行十次过期扫描(100ms一次),过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。
1、从过期字典中随机 20 个 key;
2、删除这 20 个 key 中已经过期的 key;
3、如果过期的 key 比率超过 1/4,那就重复步骤 1;
redis默认是每隔 100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。
注意这里是随机抽取的。为什么要随机呢?
你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载。
惰性删除:
所谓惰性策略就是在客户端访问这个key的时候,redis对key的过期时间进行检查,如果过期了就立即删除,不会给你返回任何东西。
为啥需要两种删除策略呢?
定期删除可能会导致很多过期key到了时间并没有被删除掉。
所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。
这就是所谓的惰性删除,即当你主动去查过期的key时,如果发现key过期了,就立即进行删除,不返回任何东西.
总结:定期删除是集中处理,惰性删除是零散处理
2):Redis 内存爆满 淘汰置换策略
当 Redis 内存使用达到 maxmemory
时,需要选择设置好的 maxmemory-policy
进行对老数据的置换。
下面是可以选择的置换策略:
不同于之前的版本,redis5.0为我们提供了八个不同的内存置换策略;很早之前提供了6种。
- volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
- volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
- volatile-lfu:从已设置过期时间的数据集挑选使用频率最低的数据淘汰。
- allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
- allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据,这也是默认策略。
意思是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失。
这八种大体上可以分为4中:
- lru(Least Recently Used,最近最少使用)
- lfu(Least Frequently Used,最不经常使用)、
- random(随机)
- ttl
设置 maxmemory-policy
的方法 和 设置 maxmemory
方法类似,通过 redis.conf
或是通过 CONFIG SET
动态修改。
选择合适的置换策略是很重要的,这主要取决于你的应用的访问模式,当然你也可以动态的修改置换策略;
并通过用 Redis 命令——INFO
去输出 cache 的命中率情况,进而可以对置换策略进行调优。
置换策略是如何工作的?
- 客户端执行一条新命令,导致数据库需要增加数据(比如set key value)
- Redis会检查内存使用,如果内存使用超过 maxmemory,就会按照置换策略删除一些 key
- 新的命令执行成功
我们持续的写数据会导致内存达到或超出上限 maxmemory,但是置换策略会将内存使用降低到上限以下。
如果一次需要使用很多的内存(比如一次写入一个很大的set),那么,Redis 的内存使用可能超出最大内存限制一段时间。
LRU 算法机制:
LRU算法的全称叫做Least Recently Used,也就是最近最少使用原则来进行数据的淘汰算法。
其原理就是,会将数据放入到一个链表中,当链表中的某个元素被访问时,这个元素就被会提到链表的前面,其他元素,默认向后移动;
当这个时候我们想缓存中新增一个元素时,也会被增加到链表的头部,那么尾部的最后一个元素就被淘汰了。
lru的实现思想就是:就是刚被访问的数据,在接下来的时间里,更容易被再次访问,而一段时间没有被访问的数据,之后也不会再次访问。
但是要将redis的全部数据都放入这样一个链表中的话,redis的数据被频繁访问时,需要不停的移动链表的位置,降低redis的性能。
所以redis对LRU算法进行了优化 ↓
在redis中,会给每个数据记录一个最近访问的时间戳(记录在RedisObject的lru字段中),
当需要进行数据淘汰时,redis就随机筛选出N个数据放入到候选集合中去,然后比较这N个数据中的lru的值,最小的就会被淘汰。
当再次需要淘汰数据时,这个时候筛选数据放入到第一次创建的淘汰集合中,那么这次筛选就不在是随机筛选,而是能进入候选集合的数据的 lru 字段值必须小于候选集合中最小的 lru 值,
然后再次将最小的lru的值的数据进行淘汰。
其中N的配置项为:
maxmemory-samples 100 # 表示N为100
LFU 算法机制:
LFU(Least frequently used)称为最近使用最少的数据将被淘汰,redis在就是在LRU的基础上增加一个次数统计。
其步骤就是根据数据的访问次数进行筛选,淘汰访问次数少的数据,如果访问次数相同,在根据访问时间进行比较,淘汰访问时间久远的数据。
redis中的实现方式:就是在RedisObject的字段lru上,拆分为两个部分:
- ldt值:lru字段的前16bit位,还是用来表示时间戳。
- counter值:lru字段的后8bit位,用来表示数据的访问次数。
当 LFU 策略筛选数据时,Redis 会在候选集合中,根据数据 lru 字段的后 8bit 选择访问次数最少的数据进行淘汰。
当访问次数相同时,再根据 lru 字段的前 16bit 值大小,选择访问时间最久远的数据进行淘汰。
但是8个bit位,最大只能记录255的值,但是redis中的数据,有时候会被访问成千上万次,那么这个问题如何进行解决呢?
redis对计数进行了优化,并不是数据被访问一次,counter就会被加1,而是遵循如下规则:↓
当数据被访问一次时,首先用计数器当前的值乘以配置项lfu_log_factor再加1,再取倒数得到一个p值然后把这个p值和一个取值范围在(0,1)的一个随机数r,进行比大小,只有p值大于r时,counter的值才会被加一
lfu-log-factor可以调整计数器counter的增长速度,lfu-log-factor越大,counter增长的越慢。 lfu-decay-time是一个以分钟为单位的数值,可以调整counter的减少速度
#redis部分源码展示 double r = (double)rand()/RAND_MAX; double p = 1.0/(baseval*server.lfu_log_factor+1); if (r < p) counter++;
其中 baseval是计数器的当前值。计数器的默认初始值为5(由代码中的 LFU_INIT_VAL 常量设置),并不是为0,这样可以避免数据刚进入缓存,就因为访问次数少而被立即淘汰。
当lfu_log_factor取不同的值时,实际访问次数下,counter的值的变化情况:
在实际的使用场景中,还会有这样一种情况,某些数据可能一开始会被大量的访问,但是过了时间段后,就不会再被访问。
如果这个时候counter的值很大,就算后续不会被访问,也就不会被redis进行数据淘汰。
针对这种情况,在redis中,设计了counter的衰减策略。其实现就是根据lfu_decay_time的配置值,来控制访问次数的衰减。
其流程如下:
- lfu会计算当前时间和数据最近一次访问的时间差值,并将这个差值换算为分钟单位。
- 然后在将这个差值除以lfu_decay_time值,得到的就是我们需要减去的值
- 然后再讲counter的值减去这个值
这样就可以保证在一段时间后,可以淘汰这部分数据。
Redis 的淘汰策略怎么选:
一般来说,有这样一些常用的经验:
- 在所有的 key 都是最近最经常使用,那么就需要选择 allkeys-lru 进行置换最近最不经常使用的 key,如果你不确定使用哪种策略,那么推荐使用 allkeys-lru
- 如果所有的 key 的访问概率都是差不多的,那么可以选用 allkeys-random 策略去置换数据
- 如果对数据有足够的了解,能够为 key 指定 hint(通过expire/ttl指定),那么可以选择 volatile-ttl 进行置换
volatile-lru
和 volatile-random
经常在一个Redis实例既做cache又做持久化的情况下用到,然而,更好的选择使用两个Redis实例来解决这个问题。
设置是失效时间 expire
会占用一些内存,而采用 allkeys-lru
就没有必要设置失效时间,进而更有效的利用内存。
Redis++:Redis 内存爆满 之 淘汰策略的更多相关文章
- redis 提供 6种数据淘汰策略
淘汰策略的原因 在 redis 中,允许用户设置最大使用内存大小 server.maxmemory,在内存限定的情况下是很有用的.譬如,在一台 8G 机子上部署了 4 个 redis 服务点,每一个服 ...
- Redis达到最大占用内存后的淘汰策略
1. 查询Redis最大占用内存 # 查询最大占用内存 config get maxmemory # 为0时在64操作系统中不限制内存,在32位操作系统中最大为3GB 2. Redis设置最大占用内存 ...
- redis的maxmemory设置以及淘汰策略介绍
转载地址:http://www.2cto.com/database/201507/420889.html redis的maxmemory参数用于控制redis可使用的最大内存容量.如果超过maxmem ...
- Redis系列之----Redis的过期设置及淘汰策略
Redis的过期时间机制和内存淘汰策略 Redis的数据是存储在内存中的,而服务器的内存大小是有限制的,除非宕机,否则这些数据会一直存在,对于一些不再使用的key,也应当进行删除,否则会浪费内存 ...
- Redis篇:持久化、淘汰策略,缓存失效策略
关注公众号,一起交流,微信搜一搜: 潜行前行 redis 持久化 redis 的数据是保存再系统内存里面的.持久化就是把内存的数据转移到磁盘中,redis 的持久化策略有两种:RDB.AOF RDB ...
- redis数据淘汰策略
概述 在 redis 中,允许用户设置最大使用内存大小 server.maxmemory,在内存限定的情况下是很有用的.譬如,在一台 8G 机子上部署了 4 个 redis 服务点,每一个服务点分配 ...
- 动手实现 LRU 算法,以及 Caffeine 和 Redis 中的缓存淘汰策略
我是风筝,公众号「古时的风筝」. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 那天我在 LeetCode 上刷到一道 LRU 缓存机制的问题, ...
- redis 淘汰策略有哪些?
noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息. 大多数写命令都会导致占用更多的内存(有极少数会例外, 如 DEL ). allkeys-lru: 所 ...
- Redis的过期策略和内存淘汰策略(转)
Redis的过期策略 我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间.Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理. ...
随机推荐
- Webstorm 快速补全
el-row>el-col*3>[:span='7'] 按Tab <el-row> <el-col> <div :span="">& ...
- SAS 常用字符串函数
原文链接:https://www.cnblogs.com/snoopy1866/p/15085466.html CAT(item-1 <, -, item-n>) : 在保留首尾空格的情况 ...
- Pdb— Python的调试器
参考:Pdb- Python的调试器 pdb 模块定义了一个交互式源代码调试器,用于 Python 程序.它支持在源码行间设置(有条件的)断点和单步执行,检视堆栈帧,列出源码列表,以及在任何堆栈帧的上 ...
- Java基础——JavaDoc生成文档
JavaDoc生成文档 package Top1; /** * @author lwt * @version 1.0 * @since 1.8 * */ public class ...
- 乘风破浪,遇见Android Jetpack之Compose声明式UI开发工具包,逐渐大一统的原生UI绘制体系
什么是Android Jetpack https://developer.android.com/jetpack Android Jetpack是一个由多个库组成的套件,可帮助开发者遵循最佳做法.减少 ...
- HDFS中NameNode工作机制
引言 NameNode: 存储元数据 管理整个HDFS集群 DataNode: 存储数据的block SecondaryNameNode: 辅助HDFS完成一些事情 NameNode和Secondar ...
- CentOS的crond系统定时服务
crond 服务管理 [root@node01 ~]# service crond start (启动服务) [root@node01 ~]# service crond stop ...
- SpringBoot-技术专区-用正确的姿势如何用外置tomcat配置及运行(Tomcat优化分析)
前提概要 在特别特殊的时候,我们可能需要外置tomcat去运行程序,例如alitomcat等特殊场景,方便我们去定时化开发项目或者其他特殊场景. 外置tomcat执行 pom.xml文件首先更改打包方 ...
- shell——sed编辑器
目录 一.sed编辑器 1.1.sed编辑器工作流程 读取: 执行: 显示: 1.2.格式 1.3.常用选项 1.4.常用操作 1.5.替换 一.sed编辑器 sed是一种流编辑器,流编辑器会在编辑器 ...
- SQL 练习25
查询同名学生名单,并统计同名人数 SELECT sname,COUNT(sname) 同名人数 from Student GROUP BY sname HAVING COUNT(sname)>1