Redis内存淘汰策略

本文将详细介绍Redis内存淘汰策略

我们知道 Redis 是分布式内存数据库,基于内存运行,访问速度非常快。可是,市面上比较好的服务器内存也不过几百G(像那种上 T 内存大小的服务器列外),那么能存多少数据呢?当服务器内存占用满了之后该怎么办呢?Redis 的内存是否可以设置限制? Redis 过期的 key 是怎么从内存中删除的?

Redis 内存大小限制

在 Redis 中是可以设置内存最大限制的,因此我们不用担心 Redis 占满服务器的内存,从而影响其他服务。通过配置 maxmemory 参数就可以实现内存大小限制,语法如下:

# 配置文件
maxmemory <bytes>

下面的 maxmemory 参数写法均合法:

maxmemory 1024000
maxmemory 1GB
maxmemory 1G
maxmemory 1024KB
maxmemory 1024K
maxmemory 1024MB

除了修改 redis.config 的 maxmemory 参数外,还可以通过命令行修改内存大小限制,例如:

# 命令行
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 都是近似随机算法;

在 redis 中,可以使用 maxmemory-policy 参数配置淘汰策略,例如:

(1)修改 redis.conf 配置文件

# 配置文件
maxmemory-policy noeviction

(2)通过 redis 命令行

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(最近最不经常使用)三种,Redis 使用了 LRU 和 LFU 淘汰策略。

FIFO算法

按照 “先进先出(First In,First Out)” 的原理淘汰数据,正好符合队列的特性,数据结构上使用队列 Queue 来实现。如下图:

Redis内存淘汰策略

LRU算法

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

LRU 算法的常见实现方式为链表:新数据放在链表头部,链表中的数据被访问就移动到链头,链表满的时候从链表尾部移出数据。如下图:

Redis内存淘汰策略

而在 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算法的常见实现方式为链表:新数据放在链表尾部 ,链表中的数据按照被访问次数降序排列,访问次数相同的按最近访问时间降序排列,链表满的时候从链表尾部移出数据。如下图:

Redis内存淘汰策略

过期删除策略

前面介绍的 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 命令;

如果在胜利前却步,往往只会拥抱失败;如果在困难时坚持,常常会获得新的成功。
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号