[转帖]Redis系列(十七)、Redis中的内存淘汰策略和过期删除策略

redis,系列,十七,内存,淘汰,策略,过期,删除 · 浏览次数 : 0

小编点评

**内存淘汰策略** **LRU算法** * 设置了过期时间的key使用LRU算法淘汰。 * LRU算法优先考虑最近访问的key。 * 当LRU算法下,如果一个热点数据最近很少访问,而非热点数据近期访问了,就会误把热点数据淘汰而留下了非热点数据,因此在Redis4.x中新增了LFU算法。 **LFU算法** * 表示最不经常使用。 * 它是根据数据的历史访问频率来淘汰数据。 * 当LRU算法下,如果一个热点数据最近很少访问,而非热点数据近期访问了,就会误把热点数据淘汰而留下了非热点数据,因此在Redis4.x中新增了LFU算法。 **过期删除策略** * 当key被访问时检查该key的过期时间,若已过期则删除。 * 惰性删除:当key被访问时检查该key的过期时间,若已过期则删除; * 定期删除:每隔一段时间,随机检查设置了过期的key并删除已过期的key; * AOF和RDB的过期删除策略: 在RDB持久化模式中我们可以使用save和bgsave命令进行数据持久化操作,而在AOF持久化模式中使用rewriteaof和bgrewriteaof命令进行持久化操作。

正文

我们知道Redis是分布式内存数据库,基于内存运行,可是有没有想过比较好的服务器内存也不过几百G,能存多少数据呢,当内存占用满了之后该怎么办呢?Redis的内存是否可以设置限制? 过期的key是怎么从内存中删除的?不要怕,本篇我们一起来看一下Redis的内存淘汰策略是如何释放内存的,以及过期的key是如何从内存中删除的。


目录

介绍

内存淘汰策略

LRU算法

LFU算法

​过期删除策略

AOF和RDB的过期删除策略


Redis6系列文章

Redis系列(一)、CentOS7下安装Redis6.0.3稳定版

Redis系列(二)、数据类型之字符串String 

Redis系列(三)、数据类型之哈希Hash

Redis系列(四)、数据类型之列表List

Redis系列(五)、数据类型之无序集合Set

Redis系列(六)、数据类型之有序集合ZSet(sorted_set)

Redis系列(七)、常用key命令

Redis系列(八)、常用服务器命令 

Redis系列(九)、Redis的“事务”及Lua脚本操作

Redis系列(十)、详解Redis持久化方式AOF、RDB以及混合持久化

Redis系列(十一)、Redis6新特性之ACL安全策略(用户权限管理)

Redis系列(十二)、Redis6集群搭建及原理(主从、哨兵、集群)

Redis系列(十三)、pub/sub发布与订阅(对比List和Kafka)

Redis系列(十四)、Redis6新特性之RESP3与客户端缓存(Client side caching)

Redis系列(十五)、Redis6新特性之集群代理(Cluster Proxy)

Redis系列(十六)、Redis6新特性之IO多线程

介绍

开篇提到Redis是基于内存的数据库,当内存满了的时候会发生什么呢?Redis的内存是否可以设置限制? 过期的key是怎么从内存中删除的?

其实在Redis中是可以设置内存最大限制的,因此我们不用担心Redis占满机器的内存影响其他服务,这个参数maxmemory是可以配置的:

  1. #配置文件
  2. maxmemory <bytes>
  3. 下面的写法均合法:
  4. maxmemory 1024000
  5. maxmemory 1GB
  6. maxmemory 1G
  7. maxmemory 1024KB
  8. maxmemory 1024K
  9. maxmemory 1024MB
  10. ...
  11. #命令行
  12. 127.0.0.1:6379> config get maxmemory
  13. 1) "maxmemory"
  14. 2) "0"
  15. 127.0.0.1:6379> config set maxmemory 1GB
  16. OK
  17. 127.0.0.1:6379> config get maxmemory
  18. 1) "maxmemory"
  19. 2) "1073741824"

maxmemory参数默认值为0。因32位系统支持的最大内存为4GB,所以在32位系统上Redis的默认最大内存限制为3GB;在64位系统上默认Redis最大内存即为物理机的可用内存;

内存淘汰策略

Redis中共有下面八种内存淘汰策略

  • volatile-lru:设置了过期时间的key使用LRU算法淘汰;
  • allkeys-lru:所有key使用LRU算法淘汰;
  • volatile-lfu:设置了过期时间的key使用LFU算法淘汰;
  • allkeys-lfu:所有key使用LFU算法淘汰;
  • volatile-random:设置了过期时间的key使用随机淘汰;
  • allkeys-random:所有key使用随机淘汰;
  • volatile-ttl:设置了过期时间的key根据过期时间淘汰,越早过期越早淘汰;
  • noeviction:默认策略,当内存达到设置的最大值时,所有申请内存的操作都会报错(如set,lpush等),只读操作如get命令可以正常执行;

LRU、LFU和volatile-ttl都是近似随机算法;

使用下面的参数maxmemory-policy配置淘汰策略:

  1. #配置文件
  2. maxmemory-policy noeviction
  3. #命令行
  4. 127.0.0.1:6379> config get maxmemory-policy
  5. 1) "maxmemory-policy"
  6. 2) "noeviction"
  7. 127.0.0.1:6379> config set maxmemory-policy allkeys-random
  8. OK
  9. 127.0.0.1:6379> config get maxmemory-policy
  10. 1) "maxmemory-policy"
  11. 2) "allkeys-random"

在缓存的内存淘汰策略中有FIFO、LRU、LFU三种,其中LRU和LFU是Redis在使用的。

FIFO是最简单的淘汰策略,遵循着先进先出的原则,这里简单提一下:

LRU算法

LRU(Least Recently Used)表示最近最少使用,该算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

LRU算法的常见实现方式为链表:

新数据放在链表头部 ,链表中的数据被访问就移动到链头,链表满的时候从链表尾部移出数据。

而在Redis中使用的是近似LRU算法,为什么说是近似呢?Redis中是随机采样5个(可以修改参数maxmemory-samples配置)key,然后从中选择访问时间最早的key进行淘汰,因此当采样key的数量与Redis库中key的数量越接近,淘汰的规则就越接近LRU算法。但官方推荐5个就足够了,最多不超过10个,越大就越消耗CPU的资源。

但在LRU算法下,如果一个热点数据最近很少访问,而非热点数据近期访问了,就会误把热点数据淘汰而留下了非热点数据,因此在Redis4.x中新增了LFU算法。

在LRU算法下,Redis会为每个key新增一个3字节的内存空间用于存储key的访问时间;

LFU算法

LFU(Least Frequently Used)表示最不经常使用,它是根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。

LFU算法反映了一个key的热度情况,不会因LRU算法的偶尔一次被访问被误认为是热点数据。

LFU算法的常见实现方式为链表:

新数据放在链表尾部 ,链表中的数据按照被访问次数降序排列,访问次数相同的按最近访问时间降序排列,链表满的时候从链表尾部移出数据。


过期删除策略

前面介绍的LRU和LFU算法都是在Redis内存占用满的情况下的淘汰策略,那么当内存没占满时在Redis中过期的key是如何从内存中删除以达到优化内存占用的呢? 

官网:https://redis.io/commands/expire#expire-accuracy 

在Redis中过期的key不会立刻从内存中删除,而是会同时以下面两种策略进行删除:

  • 惰性删除:当key被访问时检查该key的过期时间,若已过期则删除;已过期未被访问的数据仍保持在内存中,消耗内存资源;
  • 定期删除:每隔一段时间,随机检查设置了过期的key并删除已过期的key;维护定时器消耗CPU资源;

Redis每10秒进行一次过期扫描:

  1. 随机取20个设置了过期策略的key;
  2. 检查20个key中过期时间中已过期的key并删除;
  3. 如果有超过25%的key已过期则重复第一步;

这种循环随机操作会持续到过期key可能仅占全部key的25%以下时,并且为了保证不会出现循环过多的情况,默认扫描时间不会超过25ms;

AOF和RDB的过期删除策略

前面介绍了Redis的持久化策略RDB和AOF,当Redis中的key已过期未删除时,如果进行RDB和AOF的持久化操作时候会怎么操作呢?

  • 在RDB持久化模式中我们可以使用savebgsave命令进行数据持久化操作
  • 在AOF持久化模式中使用rewriteaofbgrewriteaof命令进行持久化操作

这四个命令都不会将过期key持久化到RDB文件或AOF文件中,可以保证重启服务时不会将过期key载入Redis。

为了保证一致性,在AOF持久化模式中,当key过期时候,会同时发送DEL命令给AOF文件和所有节点;

从节点不会主动的删除过期key除非它升级为主节点或收到主节点发来的DEL命令;

 

希望本文对你有帮助,请点个赞鼓励一下作者吧~ 谢谢!

 

与[转帖]Redis系列(十七)、Redis中的内存淘汰策略和过期删除策略相似的内容:

[转帖]Redis系列(十七)、Redis中的内存淘汰策略和过期删除策略

我们知道Redis是分布式内存数据库,基于内存运行,可是有没有想过比较好的服务器内存也不过几百G,能存多少数据呢,当内存占用满了之后该怎么办呢?Redis的内存是否可以设置限制? 过期的key是怎么从内存中删除的?不要怕,本篇我们一起来看一下Redis的内存淘汰策略是如何释放内存的,以及过期的key

[转帖]Redis系列(十五)、Redis6新特性之集群代理(Cluster Proxy)

在之前的文章中介绍了Redis6的集群搭建和原理,我们可以使用dummy和smart客户端连接集群,本篇介绍Redis6新增的一个功能:集群代理。客户端不需要知道集群中的具体节点个数和主从身份,可以直接通过代理访问集群,对于客户端来说通过集群代理访问的集群就和单机的Redis一样,因此也能解决很多集

[转帖]Redis系列(十五)、Redis6新特性之集群代理(Cluster Proxy)

在之前的文章中介绍了Redis6的集群搭建和原理,我们可以使用dummy和smart客户端连接集群,本篇介绍Redis6新增的一个功能:集群代理。客户端不需要知道集群中的具体节点个数和主从身份,可以直接通过代理访问集群,对于客户端来说通过集群代理访问的集群就和单机的Redis一样,因此也能解决很多集

[转帖]MySQL十六:36张图理解Buffer Pool

https://www.cnblogs.com/yunlongn/p/16630257.html 转载~ 在应用系统中,我们为加速数据访问,会把高频的数据放在「缓存」(Redis、MongoDB)里,减轻数据库的压力。 在操作系统中,为了减少磁盘IO,引入了「缓冲池」(buffer pool)机制。

[转帖]Redis系列(十六)、Redis6新特性之IO多线程

https://blog.csdn.net/wsdc0521/article/details/106766587 终于,Redis的多线程版本横空出世,大大提高了并发,本篇就带大家来看看什么是IO多线程,和我们理解的多线程有什么区别,与Memcached的多线程又有什么区别。 目录 介绍 为什么Re

[转帖]抛砖系列之redis监控命令

处理一下.. 前言 redis是一款非常流行的kv数据库,以高性能著称,其高吞吐、低延迟等特性让广大开发者趋之若鹜,每每看到别人发出的redis故障报告都让我产生一种居安思危,以史为鉴的危机感,恰逢今年十一西安烟雨不断,抽时间学习了几个redis监控命令,和大家分享一波。 redis-cli --s

[转帖]【Redis系列】Redis发布版本历史及特性

目录 概述Redis2.6Redis2.8Redis3.0Redis3.2Redis4.0Redis5.0Redis6.0Redis7.0 概述 Redis 使用标准版本标记进行版本控制:major.minor.patchlevel。 偶数的版本号表示稳定的版本, 例如 1.2,2.0,2.2,2.

[转帖]玩转REDIS-删除了两百万KEY,为什么内存依旧未释放?

https://www.freesion.com/article/87101375552/ 《玩转Redis》系列文章主要讲述Redis的基础及中高级应用。本文是《玩转Redis》系列第【12】篇,最新系列文章请前往公众号“zxiaofan”(点我点我)查看,或百度搜索“玩转Redis zxiaof

[转帖]Redis 运维实战 第09期:Redis 规范

https://cloud.tencent.com/developer/article/1986835 这是专栏《Redis 运维实战》的最后一篇,感谢您的阅读。也感谢 9 篇文章的审稿人:无为,提出了多个修改建议,让文章内容更全面。 由于能力有限,系列文章难免会存在错误或者遗漏,如果您有任何建议,

[转帖]Redis manager快速启动

2.x 快速启动 系统环境要求 JDK8+, OpenJDK8+ Maven3 Mysql5.7+ 启动命令 在 redis-manager 目录下执行 ./bin/start.sh 准备数据库 系统仅需要创建数据库即可,相关表会在项目启动时自动创建 CREATE DATABASE `redis_m