Redis内存满了怎么办?

如果长期用Redis做缓存,总有一天Redis的内存会满的。怎么处理?

在Redis配置文件redis.conf文件中,maxmemory的大小参数配置如下:

如果实际存储超过Redis的配置参数的大小,Redis中有一个消除策略,消除需要消除的键,为新键值整理出一块干净的内存。

Redis提供了六种消除策略,其中默认为noeviction,这六种策略中的消除策略如下:

LRU(最近最少使用)表示最近最少使用的键,即最近最少访问的键。该算法根据数据的历史访问记录来删除数据。

它的核心思想是,如果一个键值最近很少被使用,那么将来也很少被访问。

其实Redis实现的LRU并不是真正的LRU算法,也就是名义上我们用LRU算法来消除密钥,但实际上被消除的密钥不一定真的是最长无用的。

Redis使用一种近似LRU算法,通过随机收集的方式消除密钥,每次随机选择五个密钥,然后消除最近最少使用的密钥。

这里的五个键只是默认的数字,具体数字也可以在配置文件中配置,如下图所示:

当近似LRU算法较大时,它将更接近真实的LRU算法。可以理解为,值越大,数据就越完整,被淘汰的数据就越接近最近最少使用的数据。

那么为了实现按时间的LRU算法,Redis必须为每个键增加一个额外的内存空间来存储每个键的时间,大小为3字节。

在Redis 3.0中,对近似LRU算法进行了优化,在Redis中会维护一个大小为16的候选池的内存。

第一次随机抽取采样数据时,将数据放入候选池,候选池中的数据按时间排序。

当数据第二次被选中后,只有在候选池中少于最小时间的数据才会被放入候选池。

当某一时刻候选池的数据满了,时间最长的键就会被挤出候选池。淘汰时,直接从候选池中选择最近访问时间最短的键进行淘汰。

这样做的目的是选择最近似乎最少访问的键值,以便正确地消除该键值,因为随机选择的样本中的最小时间可能不是真正的最小时间。

但是,LRU算法有一个缺点:如果一个键值以前没有被访问过,但最近被访问过,它将被认为是热数据,不会被消除。

但是,有些数据之前被频繁访问过,而最近没有,可能会导致这些数据被剔除,从而导致对热点数据的误判和剔除。

那么在Redis 4.0中,除了LRU算法之外,又增加了一个新的LFU算法,那么LFU算法是什么呢?

LFU(Least frequency Used)是指最近频繁使用的关键字,即最近一段时间内频繁访问的关键字,它以最近一段时间内被访问的次数的频率为标准。

其核心思想是:根据key最近访问的频率,先淘汰访问次数少的key,反之亦然。

LFU算法反映了一个键的流行程度,不会因为LRU算法偶尔被访问一次而被认为是热门数据。

LFU算法支持易变-LFU策略和allkeys-lfu策略。

Redis中有三种删除操作。这一战略是:

坚持Redis有两种方式:RDB和AOF。

在RDB中,内存中某个时间点的数据副本是以快照的形式获得的。创建RDB文件时,可以通过save和bgsave命令创建RDB文件。

这两个命令都不会将过期的密钥保存到RDB文件中,这样也可以达到删除过期密钥的效果。

在启动Redis时加载RDB文件时,主服务器不会加载过期的密钥,而从服务器会加载过期的密钥。

在AOF模式下,Redis为重写提供了优化措施,执行的命令分别是REWRITEAOF和BGREWRITEAOF。这两个命令都不会将过期的密钥写入AOF文件,而且它们还可以删除过期的密钥。

RDB是一种快照存储持久化方法,具体是将Redis在某一时刻的内存数据保存到硬盘上的一个文件中。默认情况下,保存的文件名为dump.rdb,当Redis服务器启动时,dump.rdb文件的数据将被重新加载到内存中,以恢复数据。

打开RBD持续模式

启动rdb持久性的方法很简单。客户端可以向Redis服务器发送save或bgsave命令,让服务器生成RDB文件,或者通过服务器配置文件指定触发RDB的条件。

保存命令是同步操作。

当客户端向服务器发送保存命令进行持久化时,服务器会在保存命令后阻塞其他客户端的请求,直到数据同步完成。

与save命令不同,bgsave命令是一个异步操作。

当客户端发送服务发出bgsave命令时,Redis服务器的主进程会分叉一个子进程来解决数据同步问题。将数据保存到rdb文件后,子流程将退出。

因此,与save命令相比,Redis服务器在处理bgsave时使用一个子线程写IO,主进程仍然可以接收其他请求,但forks子进程是同步的,因此forks子进程不能接收其他请求,这意味着如果一个fork子进程耗时过长(通常非常快),bgsave命令仍然会阻塞其他客户的请求。

除了通过客户端发送命令,还有另一种方式,即在Redis配置文件中保存指定触发RDB持久化的条件,比如在几秒钟内达到至少几个写操作时启动RDB数据同步。

例如,我们可以在配置文件redis.conf中指定以下选项:

然后在服务器启动时加载配置文件。

这种通过服务器配置文件触发rdb的方式类似于bgsave命令。当达到触发条件时,forks的子进程将同步数据。但是最好不要这样触发RDB持久化,因为触发时间太短容易频繁写RDB文件,影响服务器性能,时间设置太长会造成数据丢失。

本文介绍了服务器生成rdb文件的三种方式,无论是主进程生成还是子进程生成,流程如下:

Redis的另一种持久方式:AOF(仅附加文件)。

与RDB存储某个时刻的快照不同,aof持久化记录了每一个从客户端到服务器的写操作命令,并将这些写操作保存到Redis协议中带后缀的aof文件的末尾。当Redis服务器重新启动时,它将加载并运行AOF文件命令来恢复数据。

默认情况下,Redis不启用AOF持久性。我们可以在配置文件中启用它,并对其进行更详细的配置,例如下面的redis.conf文件:

在上面的配置文件中,我们可以通过appendfsync选项来指定写策略,它有三个选项。

当客户端的每个写操作都保存到aof文件中时,这种策略是安全的,但是每个写请求都有IO操作,所以也很慢。

Appendfsync的默认写入策略是每秒写入一次aof文件,因此最多可能会丢失1的数据。

Redis服务器不负责写入aof,但是操作系统处理何时写入aof文件。更快,但也是最不安全的选择,不推荐。

AOF将客户端的每个写操作附加到aof文件的末尾,例如多次执行某个键的incr命令。此时,aof保存aof文件中的每个命令,aof文件会变得非常大。

aof文件太大,加载aof文件来恢复数据时会非常慢。为了解决这个问题,Redis支持重写aof文件。通过重写aof,可以生成用于恢复当前数据的最小命令集,例如上述示例中的许多命令,其可以被重写为:

您可以通过redis.conf配置文件中的选项no-appendfsync-on-rewrite来设置是否打开重写。这个方法每次fsync都会重写,会影响服务器的性能,所以默认值是no,不推荐。

客户端向服务器发送bgrewriteaof命令,或者它可以让服务器重写aof。

Aof重写也是一个异步操作,也就是说,如果你想写入一个AOF文件,Redis主进程将由一个forks子进程处理,如下所示:

在写入aof日志文件时,如果Redis服务器关闭,aof日志文件将出现格式错误。当Redis服务器重新启动时,Redis服务器将拒绝加载这个aof文件。您可以通过以下步骤修复aof并还原数据。

AOF只追加日志文件,因此对服务器性能影响很小,比RDB快,并且消耗的内存更少。

我们可以从几个方面比较RDB和AOF。在应用中,我们应该根据自己的实际需要选择RDB或AOF。其实如果想要数据足够安全的话,两种方式都可以打开,但是两种持久方式同时IO操作会严重影响服务器的性能,所以有时候我们不得不做出选择。

当RDB和AOF都打开时,Redis会优先使用AOF日志来恢复数据,因为AOF保存的文件比RDB文件更完整。