Redis过期策略以及Redis的内存淘汰机制
此篇介绍了Redis过期策略以及Redis的内存淘汰机制,从内存淘汰的8种策略,如何开启内存淘汰策略到如何选择合适的淘汰策略,对Redis的内存淘汰机制做了全方位的阐述
如何高效的使用内存对于redis来说是非常关键的,因为redis的操作都是基于内存的,而每台机器的内存大小都有限制,且全没有磁盘空间那么大
一、Redis过期策略
为了不数据占满内存,这时候我们就会想的将一些不需要永久保持的数据设置一个过期时间。
接下来我们看下以下几个问题:
如何设置key的过期时间?
redis提供了四种命令来设置key的过期时间:
(1) EXPIRE key seconds // 设置多少秒后过期
(2) EXPIREAT key timestamp 设置 key 过期时间的时间戳(unix timestamp) 以秒计
(3) PEXPIRE key milliseconds // 设置多少毫秒后过期
(4) PEXPIREAT key milliseconds-timestamp // 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计
移除redis的过期时间:
PERSIST key // 移除key的过期时间,key将保持永久
查询剩余生存时间:
TTL key // 以秒为单位,返回给定 key 的剩余生存时间
PTTL key // 以毫秒为单位返回 key 的剩余的过期时间
设置完一个key的过期时间后,到了这个过期时间,这个key保存的数据还占据着内存吗?
当key过期后,该key保存的数据还是会占据内存的,因为每当我们设置一个键的过期时间时,Redis会将该键带上过期时间存放到一个过期字典中。当key过期后,如果没有触发redis的删除策略的话,过期后的数据依然会保存在内存中的,这时候即便这个key已经过期,我们还是能够获取到这个key的数据。
redis什么时候去删除过期的数据?
redis过期删除策略通常有三种:定时删除,定期删除,惰性删除。
redis使用的是:“定期删除+惰性删除”。
(1) 定时删除
在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
优点:定时删除对内存是最友好的,能够保存内存的key一旦过期就能立即从内存中删除。
缺点:对CPU最不友好,在过期键比较多的时候,删除过期键会占用一部分 CPU 时间,对服务器的响应时间和吞吐量造成影响。
(2) 定期删除
每隔一段时间,我们就对一些key进行检查,删除里面过期的key。Redis默认每隔100ms就随机抽取部分设置了过期时间的key,检测这些key是否过期,如果过期了就将其删除。
这里有两点需要注意下:
默认的每隔100ms是在Redis的配置文件redis.conf中有一个属性"hz",默认为10,表示1s执行10次定期删除,即每隔100ms执行一次,可以修改这个配置的值来设置默认的间隔时间。
随机抽取部分,而不是全部key。因为如果Redis里面有大量key都设置了过期时间,全部都去检测一遍的话CPU负载就会很高,会浪费大量的时间在检测上面,甚至直接导致redis挂掉。所有只会抽取一部分而不会全部检查。
优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。如果执行的太少,那又和惰性删除一样了,过期键长时间占用的内存没有及时释放的话,当我们再次获取这个过期的key时,依然会返回这个key的值,就相当于这个过期时间是无效的了。
(3) 惰性删除
设置该key 过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
优点:对 CPU友好,我们只会在使用该键时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。
缺点:对内存不友好,如果一个键已经过期,但是一直没有使用,那么该键就会一直存在内存中,如果数据库中有很多这种使用不到的过期键,这些键便永远不会被删除,内存永远不会释放,从而造成内存泄漏。所以redis还引入了另一种内存淘汰机制。
二、内存淘汰机制
Redis的内存淘汰机制是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。
怎么设置redis内存大小?开启Redis 的内存淘汰机制
在redis.conf 中:
- 配置
maxmemory <bytes>,设置Redis的最大内存空间。不设定该参数默认是无限制的,但是通常会设定其为物理内存的四分之三。 - 配置
maxmemory-policy noeviction,设置淘汰策略,默认为noeviction
内存淘汰方式有哪些?
noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。(默认选项,一般不会选用)
allkeys-lru:当内存不足以容纳新写入数据时,在整个键空间中,移除最近最少使用的key。(这个是最常用的)
allkeys-lfu:当内存不足以容纳新写入数据时,在整个键空间中,移除最不经常(最少)使用的key。
allkeys-random:当内存不足以容纳新写入数据时,在整个键空间中,随机移除某个key。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
volatile-lfu:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最不经常(最少)使用的key。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
三、常用的淘汰算法
FIFO 算法(Fist in first out:先进先出)
FIFO 算法是一种比较容易实现的算法。它的思想:是基于队列的先进先出原则,最先进入的数据会被最先淘汰掉。这是最简单、最公平的一种思想。
(1)实现:维护一个FIFO队列,按照时间顺序将各数据(已分配页面)链接起来组成队列,并将置换指针指向队列的队首。再进行置换时,只需把置换指针所指的数据(页面)顺次换出,并把新加入的数据插到队尾即可。
(2)缺点:这种算法有个很严重的缺点,就是会导致缺页率增加。缺页率指的是判断一个页面置换算法优劣的指标。随着分配页面的增加,被置换的内存页面往往是被频繁访问的,因此FIFO算法会使一些页面频繁地被替换和重新申请内存,从而导致缺页率增加。由于缺页率会随着分配页面的增加而增加,使得redis的开销也逐渐增加,所以这种算法已经不再使用。
LRU算法(Least recently used:最近最少使用)
LRU算法是一种常见的缓存算法,它的思想是:最近最少使用的会被优先淘汰。如果一个数据在最近一段时间没有被访问到,那么可以认为在将来它被访问的可能性也很小。因此,当空间满时,最久没有访问的数据最先被淘汰掉。
(1)实现:最简单的实现方法是用数组+时间戳的方式,不过这样做效率较低。因此,我们可以用双向链表(LinkedList)+ 哈希表(HashMap)实现(链表用来表示位置,哈希表用来存储和查找),在Java里有对应的数据结构LinkedHashMap。
(2)缺点:它在需要淘汰时,只是随机选取有限的key进行对比,排除掉访问时间最久的元素,也就意味着它不能选择整个候选元素的最优解,只是局部最优。默认随机选取的key的数目为5,在配置文件redis.conf 中由maxmemory_samples属性的值决定,采样数量越大越接近于标准LRU算法,但也会带来性能的消耗。在Redis 3.0以后增加了LRU淘汰池,进一步提高了与标准LRU算法效果的相似度。淘汰池即维护的一个数组,数组大小等于抽样数量 maxmemory_samples,在每一次淘汰时,新随机抽取的key和淘汰池中的key进行合并,然后淘汰掉最旧的key,将剩余较旧的前面5个key放入淘汰池中待下一次循环使用。假如maxmemory_samples=5,随机抽取5个元素,淘汰池中还有5个元素,相当于变相的maxmemory_samples=10了,所以进一步提高了与LRU算法的相似度。
LFU算法(Least frequently used:最不常使用)
LFU算法的思想是:如果一个数据在最近一段时间很少被访问到,那么可以认为在将来它被访问的可能性也很小。因此,当空间满时,最小频率访问的数据最先被淘汰。
实现:如果只为每个key维护了一个计数器,每次key被访问的时候,计数器增大,计数器越大,则认为访问越频繁。
这样还是远远不够的,还会存在两个问题:
(1)因为可能存在在开始一个小时内,某个key1有100万的访问量,但是在之后的一个小时内,这个key1的访问量为0了,而在这第二个小时内另外有个key2的访问量达到了20万,虽然这20万不如前面那个key1开始那个小时的100万访问量大,但是在第二个小时内这key2的访问量远大于key1的访问量,所以在第二个小时内key1依然会优先于key2被淘汰掉。
(2)当新加入的key,由于没有被访问过,所以初始的计数器为0,如果这时候触发淘汰机制的话,就会把最先添加到key最先淘汰掉。
所以在LFU算法中维护了这个24bit的字段,不过被分成了16 bits与8 bits两部分。第一部分:高16 bits用来记录计数器的上次缩减时间,时间戳,单位精确到分钟。第二部分:低8 bits用来记录计数器的当前数值,这个数值反映了访问频率,而不是次数。
在redis.conf配置文件中还有2个属性可以调整LFU算法的执行参数:lfu-log-factor、lfu-decay-time。其中lfu-log-factor用来调整计数器counter的增长速度,lfu-log-factor越大,counter增长的越慢。lfu-decay-time是一个以分钟为单位的数值,用来调整counter的缩减速度。
LRU与LFU的选择
需要根据业务权衡到底是选择 淘汰最近最少使用(LRU) 还是选择 最不经常使用(LFU)
总的来说,无论是 LRU LFU TTL 还是Random 都是几近算法来实现的,在可靠性和性能上做了一定的平衡。还是应该在业务中主动删除没有价值的数据,或者更新某些key的过期时间等来提高Redis的性能和空间,不能过分依赖于淘汰策略。
四、总结
Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据
Redis过期策略以及Redis的内存淘汰机制的更多相关文章
- Redis 中的过期删除策略和内存淘汰机制
Redis 中 key 的过期删除策略 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 ...
- redis 的过期策略都有哪些?内存淘汰机制都有哪些?
面试题 redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现? 面试官心理分析 如果你连这个问题都不知道,上来就懵了,回答不出来,那线上你写代码的时候,想当然的认为写进 r ...
- Redis的过期策略和内存淘汰机制
过期策略 我们set key的时候,都可以给一个expire time,就是过期时间,指定这个key比如说只能存活1个小时,我们自己可以指定缓存到期就失效. 如果假设你设置一个一批key只能存活1个小 ...
- redis的过期策略以及内存淘汰机制
redis采用的是定期删除+惰性删除策略. 为什么不用定时删除策略? 定时删除,用一个定时器来负责监视key,过期则自动删除.虽然内存及时释放,但是十分消耗CPU资源.在大并发请求下,CPU要将时间应 ...
- redis过期策略和内存淘汰机制
目录 常见的删除策略 redis使用的过期策略:定期删除+惰性删除 定期删除 惰性删除 为什么要采用定期删除+惰性删除2种策略呢? redis内存淘汰机制 常见的删除策略 1.定时删除:在设置键的过期 ...
- Redis(六)--- Redis过期策略与内存淘汰机制
1.简述 关于Redis键的过期策略,首先要了解两种时间的区别,生存时间和过期时间: 生存时间:一段时长,如30秒.6000毫秒,设置键的生存时间就是设置这个键可以存在多长时间,命令有两个 expir ...
- redis过期策略与内存淘汰机制分析
过期策略: 我们在set key时,可以给一个expire time,就是过期时间 这段过期时间以后,redis对key删除使用:定期删除+惰性删除 定期删除指redis默认在100ms内随机抽取一些 ...
- 4.redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现?
作者:中华石杉 面试题 redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现? 面试官心理分析 如果你连这个问题都不知道,上来就懵了,回答不出来,那线上你写代码的时候,想当 ...
- redis的过期策略都有哪些?内存淘汰机制都有哪些?手写一下LRU代码实现?
redis的过期策略都有哪些? 设置过期时间: set key 的时候,使用expire time,就是过期时间.指定这个key比如说只能存活一个小时?10分钟?指定缓存到期就会失效. redis的过 ...
- 关于redis的几件小事(四)redis的过期策略以及内存淘汰机制
1.数据为什么会过期? 首先,要明白redis是用来做数据缓存的,不是用来做数据存储的(当然也可以当数据库用),所以数据时候过期的,过期的数据就不见了,过期主要有两种情况, ①在设置缓存数据时制定了过 ...
随机推荐
- yb课堂之订单列表接口开发 《十七》
订单列表接口开发 VideoOrderController.java VideoOrderService.java VideoOrderServiceImpl.java VideoOrderMappe ...
- 四 黑马程序员-java面向对象(上)
一.:面向对象 (1)面向对象:是基于面向过程的一种思想. 面向过程:以函数为基础,关注实现过程. 面向对象:以对象为基础,关注实现结果. (2)面向对象的思想特点: A:是一种更符合人们思考习惯的思 ...
- 解决方案 | 一个VBA代码里面非常隐蔽的错误:运行时错误“5”:无效的过程调用或参数
1 代码部分 代码功能:实现使用sumatra打开指定pdf指定页码 代码: Sub OpenPDFatPage() Dim PDFFile As String Dim PageNumber As L ...
- 题解:P10417 [蓝桥杯 2023 国 A] 第 K 小的和
分析 这道题不是板子么. 先对序列排序,然后二分答案,设当前答案为 \(x\),枚举 \(a\) 中的数,然后二分查找 \(b\) 中不大于 \(x-a\) 的元素个数,累加判断是否不大于 \(k\) ...
- CF1950B Upscaling题解
CF1950B Upscaling题解 题意 给予你一个正整数 \(n\),构造一个如图的字符矩阵. 思路 注意数据 \(1\le n \le 20\),可以发现数据很小,于是我们可以暴力模拟. 我们 ...
- ComfyUI插件:ComfyUI Impact 节点(一)
前言: 学习ComfyUI是一场持久战,而 ComfyUI Impact 是一个庞大的模块节点库,内置许多非常实用且强大的功能节点 ,例如检测器.细节强化器.预览桥.通配符.Hook.图片发送器.图片 ...
- jmeter测试udp广播(jmeter发送udp)
jmeter测试udp广播(jmeter发送udp) jmeter测试udp广播(jmeter接收udp) 先下载安装第三方插件 下载链接:https://jmeter-plugins.org/ins ...
- 【爬虫】Python获取星巴克所有产品
视频只介绍了BS4的简单使用,但我想全部获取出来 其实翻看接口,直接有一个json资源提供了这些数据,但是没有分类 import re import urllib.request from bs4 i ...
- 【SQL】列转字符串函数
还是这个需求 主界面的列表表格是直接在后台用SQL查出来的 public String getQuerySql(ElemBean condition, List<Object> param ...
- 【Vue】12 VueRouter Part2 路由与传参
[编程式导航] 我们希望在路由跳转之前执行某一些功能... <template> <div id="app"> <h2>这是App.vue组件的 ...