我们知道Redis是分布式内存数据库,基于内存运行,可是有没有想过比较好的服务器内存也不过几百G,能存多少数据呢,当内存占用满了之后该怎么办呢?Redis的内存是否可以设置限制? 过期的key是怎么从内存中删除的?不要怕,本篇我们一起来看一下Redis的内存淘汰策略是如何释放内存的,以及过期的key是如何从内存中删除的。
目录
Redis6系列文章:
Redis系列(一)、CentOS7下安装Redis6.0.3稳定版
Redis系列(六)、数据类型之有序集合ZSet(sorted_set)
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是基于内存的数据库,当内存满了的时候会发生什么呢?Redis的内存是否可以设置限制? 过期的key是怎么从内存中删除的?
其实在Redis中是可以设置内存最大限制的,因此我们不用担心Redis占满机器的内存影响其他服务,这个参数maxmemory是可以配置的:
- #配置文件
- maxmemory <bytes>
- 下面的写法均合法:
- maxmemory 1024000
- maxmemory 1GB
- maxmemory 1G
- maxmemory 1024KB
- maxmemory 1024K
- maxmemory 1024MB
- ...
-
- #命令行
- 127.0.0.1:6379> config get maxmemory
- 1) "maxmemory"
- 2) "0"
- 127.0.0.1:6379> config set maxmemory 1GB
- OK
- 127.0.0.1:6379> config get maxmemory
- 1) "maxmemory"
- 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配置淘汰策略:
- #配置文件
- maxmemory-policy noeviction
-
- #命令行
- 127.0.0.1:6379> config get maxmemory-policy
- 1) "maxmemory-policy"
- 2) "noeviction"
- 127.0.0.1:6379> config set maxmemory-policy allkeys-random
- OK
- 127.0.0.1:6379> config get maxmemory-policy
- 1) "maxmemory-policy"
- 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秒进行一次过期扫描:
- 随机取20个设置了过期策略的key;
- 检查20个key中过期时间中已过期的key并删除;
- 如果有超过25%的key已过期则重复第一步;
这种循环随机操作会持续到过期key可能仅占全部key的25%以下时,并且为了保证不会出现循环过多的情况,默认扫描时间不会超过25ms;
AOF和RDB的过期删除策略
前面介绍了Redis的持久化策略RDB和AOF,当Redis中的key已过期未删除时,如果进行RDB和AOF的持久化操作时候会怎么操作呢?
- 在RDB持久化模式中我们可以使用save和bgsave命令进行数据持久化操作
- 在AOF持久化模式中使用rewriteaof和bgrewriteaof命令进行持久化操作
这四个命令都不会将过期key持久化到RDB文件或AOF文件中,可以保证重启服务时不会将过期key载入Redis。
为了保证一致性,在AOF持久化模式中,当key过期时候,会同时发送DEL命令给AOF文件和所有节点;
从节点不会主动的删除过期key除非它升级为主节点或收到主节点发来的DEL命令;
希望本文对你有帮助,请点个赞鼓励一下作者吧~ 谢谢!