首页 > 编程知识 正文

redis增加内存,redis存储

时间:2023-05-05 00:18:03 阅读:14684 作者:1107

我们知道redis缓存的数据有过期日期。 当缓存数据无效时,redis会删除过期的数据以节省内存。 redis如何删除过期的数据? 删除过期数据的策略是什么? 这是今天我们要讨论的第一个问题。 是Redis的过期战略。

redis为什么这么快? 原因之一是redis的操作都是基于内存的。 既然是基于内存的,内存大小就有限制。 内存不足或消耗过多时该怎么办? 这是我今天要谈的第二个问题。 Redis的内存销毁机制。

另一方面,Redis到期策略Redis在设置缓存数据时指定到期日期,到期后数据将失效。 Redis如何处理这些无效的数据? 这用于Redis过期策略—— '定期删除惯性'。

1、定期删除定期删除是指Redis随机提取默认情况下每100ms过期一次的密钥,检测这些密钥是否过期,过期后删除。

100ms是怎么来的?

Redis的配置文件redis.conf具有属性“hz”。 默认值为10,1s表示执行10次定期删除。 也就是说,可以每100ms运行一次来更改此配置值。

随机抽取一些检查,多少是多少?

也由redis.conf文件的maxmemory-samples属性确定,默认值为5。

为什么不是全部而是随机抽取部分检查?

由于Redis有大量密钥过期,一旦检测到所有密钥,CPU负载就会增加,浪费大量时间进行检测,或导致Redis挂起。 一切只是提取一部分,并不是全部检查。

因为定期删除只是随机提取并检测key的一部分,所以这样做可能会导致没有大量删除过期的key。 因此,即使大量key已过了过期时间,redis的内存也可能会被大量消耗。 为了解决这个问题,redis又引入了“惯性删除战略”。

2、惰性删除惯性删除不是主动删除,而是在尝试检索某个密钥时,redis首先检测该密钥是否过期,如果没有过期则返回。 如果过期,redis将删除此密钥,不返回。

“定期删除的惯性删除”可以保证过期的密钥最终一定会被删除,但只能保证最终一定会被删除。 如果定期删除遗漏的大量过期密钥,但我们很长时间没有访问这些密钥,这些过期的密钥不是会一直存在于内存中吗? 不是一直占用着我们的内存吗? 这样不还是会耗尽redis内存吗? 由于存在这种问题,redis又引入了“内存销毁机制”来解决。

二、Redis内存销毁机制内存销毁机制当Redis内存占用量过高时,可以删除内存销毁,即部分key,保证Redis内存占用量不高。 那么,废弃哪个key? Redis目前提供8种内存处置策略,包括volatile-LFU和allkeys-lfu,这是redis 4.0或更高版本中新增的两种lfu模式。

无视觉化

如果长颈鹿笑容不足,内存不足,写入数据,新写入操作就会出错,无法写入新数据,一般不采用。

allkeys-lru

微笑内存不足长颈鹿写入数据时,删除最近最少使用的密钥是最常用的

allkeys-random

如果微笑长颈鹿写入的数据不足,请随机删除key

allkeys-lfu

如果微笑长颈鹿没有足够的内存写入数据,请删除最不常用的密钥

volatile-lru

微笑且内存不足的长颈鹿写入数据时,将删除设置为过期日期的key中最近最少使用的key。

电压随机

不能微笑的长颈鹿写入数据时,随机删除设定了有效期限的key中的某个key。

volatile-lfu

微笑且内存不足的长颈鹿写入数据时,删除已过期的密钥中最不常用(最少)的密钥

电压- Ttl

微笑且内存不足的长颈鹿写入数据时,优先删除设定了有效期限的key中有效期限最早(剩下的生存时间最短)的key。

内存销毁战略何时执行? 内存利用率过高的标准是什么?

Redis.conf配置文件中的maxmemory属性定义了redis的最大内存使用量,并在内存消耗超过maxmemory配置值时运行内存处置策略。

配置内存处置策略

redis.conf配置文件中的maxmemory-policy属性设置内存销毁机制,否则默认为no-eviction模式。

淘汰战略的执行过程

当客户端运行命令时,Redis需要增加数据。 例如,设置密钥值;

Redis检查内存使用情况,如果内存使用量超过最大内存,则按照配置的替换策略最大内存-池进行操作

icy删除一些key;

    > 再执行新的数据的set操作;

三、其他场景对过期key的处理

1、快照生成RDB文件时

    过期的key不会被保存在RDB文件中。

2、服务重启载入RDB文件时

    Master载入RDB时,文件中的未过期的键会被正常载入,过期键则会被忽略。Slave 载入RDB 时,文件中的所有键都会被载入,当主从同步时,再和Master保持一致。

3、AOF 文件写入时

    因为AOF保存的是执行过的Redis命令,所以如果redis还没有执行del,AOF文件中也不会保存del操作,当过期key被删除时,DEL 命令也会被同步到 AOF 文件中去。

4、重写AOF文件时

    执行 BGREWRITEAOF 时 ,过期的key不会被记录到 AOF 文件中。

5、主从同步时

    Master 删除 过期 Key 之后,会向所有 Slave 服务器发送一个 DEL命令,Slave 收到通知之后,会删除这些 Key。

    Slave 在读取过期键时,不会做判断删除操作,而是继续返回该键对应的值,只有当Master 发送 DEL 通知,Slave才会删除过期键,这是统一、中心化的键删除策略,保证主从服务器的数据一致性。

四、LRU&LFU算法

在上面的8种Redis内存淘汰机制中有xxxLru、xxxLfu模式,这些模式的实现其实是基于LRU或LFU算法实现的,在面试的时候面试官可能会让你简单的写一段LRU算法实现的伪代码,这要是能写出来,你懂的...

1、LRU【最近最久未使用】

标准LRU算法是这样的:它把数据存放在链表中按照“最近访问”的顺序排列,当某个key被访问时就将此key移动到链表的头部,保证了最近访问过的元素在链表的头部或前面。当链表满了之后,就将"最近最久未使用"的,即链表尾部的元素删除,再将新的元素添加至链表头部。

因为标准LRU算法需要消耗大量的内存,所以Redis采用了一种近似LRU的做法:给每个key增加一个大小为24bit的属性字段,代表最后一次被访问的时间戳。然后随机采样出5个key,淘汰掉最旧的key,直到Redis占用内存小于maxmemory为止。其中随机采样的数量可以通过Redis配置文件中的 maxmemory_samples 属性来调整,默认是5,采样数量越大越接近于标准LRU算法,但也会带来性能的消耗。

在Redis 3.0以后增加了LRU淘汰池,进一步提高了与标准LRU算法效果的相似度。淘汰池即维护的一个数组,数组大小等于抽样数量 maxmemory_samples,在每一次淘汰时,新随机抽取的key和淘汰池中的key进行合并,然后淘汰掉最旧的key,将剩余较旧的前面5个key放入淘汰池中待下一次循环使用。假如maxmemory_samples=5,随机抽取5个元素,淘汰池中还有5个元素,相当于变相的maxmemory_samples=10了,所以进一步提高了与LRU算法的相似度。

※ 利用LinkedHashMap手写一个LRU算法的简单实现!

/** * @Copyright (C), 程序员耕耘 * @ClassName: LRU * @Author: long.yuan * @Date: 2020/2/20 13:50 * @Version: V1.0 * @Description: 利用LinkedHashMap简单手写LRU算法 */public class LRU { public static void main(String[] args) { // 定义最大容量为10 final int maxSize = 10; // 参数boolean accessOrder含义:false-按照插入顺序排序;true-按照访问顺序排序。 Map<Integer, Integer> map = new LinkedHashMap<Integer, Integer>(0, 0.75f, true) { // LinkedHashMap加入新元素时会自动调用该方法,若返回true,则会删除链表尾部的元素 @Override protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { return size() > maxSize; } }; // 先往map中加入10个元素(定义的最大容量为10) for (int i = 1; i <= 10; i++) { map.put(i, i); } // 访问一下第6个元素,看看是否会排到链表的头部 map.get(6); System.out.println("发现第6个元素排到了链表的头部:" + map.toString()); // 再加数据 map.put(11, 11); System.out.println("删除链表尾部的元素,再将新的元素添加至链表头部 :" + map.toString()); }}

执行结果:

2、LFU【最近最少使用】

假设在位置※时需要删除一个元素,对比A和B,如果使用LRU,那么删除的应该是A,因为A上次被访问距现在的时间更长,但我们发现这是不合理的,因为其实A元素被访问更频繁、更热点,所以我们实际希望删除的是B,保留A,LFU就是为应对这种情况而生的。

    在Redis LFU算法中,为每个key维护了一个计数器,每次key被访问的时候,计数器增大,计数器越大,则认为访问越频繁。但其实这样会有问题,

    1、因为访问频率是动态变化的,前段时间频繁访问的key,之后也可能很少再访问(如微博热搜)。为了解决这个问题,Redis记录了每个key最后一次被访问的时间,随着时间的推移,如果某个key再没有被访问过,计数器的值也会逐渐降低。

    2、yxdxnkey问题,对于新加入缓存的key,因为还没有被访问过,计数器的值如果为0,就算这个key是热点key,因为计数器值太小,也会被淘汰机制淘汰掉。为了解决这个问题,Redis会为yxdxnkey的计数器设置一个初始值。

    上面说过在Redis LRU算法中,会给每个key维护一个大小为24bit的属性字段,代表最后一次被访问的时间戳。在LFU中也维护了这个24bit的字段,不过被分成了16 bits与8 bits两部分:

        16 bits      8 bits

+--------------------+------------+

+ Last decr time | LOG_C  |

+--------------------+-----------+

其中高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的缩减速度。

 

感兴趣的小伙伴可以关注一下博主的公众号,1W+技术人的选择,致力于原创技术干货,包含Redis、RabbitMQ、Kafka、SpringBoot、SpringCloud、ELK等热门技术的学习&资料。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。