redis报错:MISCONF Redis is configured to save RDB snapshots

文章讲述了Redis连接时遇到的内存不足问题,涉及Redis的内存策略、RDB快照创建过程中的内存需求、以及如何通过配置`maxmemory`和`maxmemory-policy`解决内存溢出。作者还介绍了如何在Java中设置键的过期时间和调整Linux内核参数以优化Redis内存使用。
摘要由CSDN通过智能技术生成

redis-cli连接报错MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.

应用连接报错如下
Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.

根据提示redis保存RDB快照报错,应该是无法写盘,那么可能是硬盘或者内存爆了

检查了硬盘发现并没有问题,那么只剩下内存了
image.png
总内存:32G
已使用19G
redis 占用内存14G

为什么系统明明还剩13GB的内存,Redis会说内存不够呢?

网上查了一下,有人也遇到类似的问题,简单地说:Redis在保存数据到硬盘时为了避免主进程假死,需要Fork一份主进程,然后在Fork进程内完成数据保存到硬盘的操作,如果主进程使用了14GB的内存,Fork子进程的时候需要额外的14GB,此时内存就不够了,Fork失败,进而数据保存硬盘也失败了。

那为什么用到这么大的内存?开发代码逻辑也存在一定问题,key并没有添加过期时间,所以 key 应该加一下过期时间, 下方会提到key的过期回收机制

redis错误:LOADING Redis is loading the dataset in memory
原因是,redis使用的内存超过操作系统一半的内存查看文件占的内存

可以在redis.conf 添加下面两个配置限制内存
maxmemory和maxmemory-policy

我再详细解释下这两个参数

  1. maxmemory:这个参数用于设置 Redis 可以使用的最大内存容量。当 Redis 使用的内存超过这个限制时,它将根据 maxmemory-policy 参数定义的策略来处理额外的内存需求。在你的例子中,maxmemory 4gb 表示 Redis
    最多可以使用 4GB 的内存。

  2. maxmemory-policy:这个参数定义了当达到 maxmemory 设置的内存限制时,Redis 应该如何选择和淘汰键来释放内存。不同的淘汰策略适用于不同的场景和数据模式。在你的例子中,maxmemory-policy
    volatile-lru 表示使用「最近最少使用」(Least Recently Used, LRU) 算法来淘汰那些已设置过期时间(也就是
    “volatile”)的键。

maxmemory-policy 可以有以下几种设置:

  • noeviction:不进行任何淘汰,当内存不足时会返回错误。
  • allkeys-lru:从所有键中,淘汰最近最少使用的键。
  • volatile-lru:仅从设置了过期时间的键中,淘汰最近最少使用的键。
  • allkeys-random:从所有键中随机淘汰一些键。
  • volatile-random:仅从设置了过期时间的键中随机淘汰一些键。
  • volatile-ttl:从设置了过期时间的键中,优先淘汰剩余生存时间(TTL)最短的键。
  • volatile-lfu:仅从设置了过期时间的键中,淘汰最不经常使用的键(Least Frequently Used)。
  • allkeys-lfu:从所有键中,淘汰最不经常使用的键。

所以
Redis 在创建 RDB 快照时遇到了内存不足的问题,导致写盘失败。stop-writes-on-bgsave-error yes 配置确实会在这种情况下阻止写操作以保护数据完整性。不过,直接使用 kill 命令杀掉 Redis 进程并重新启动通常不是推荐的做法,因为这样可能会导致未持久化的数据丢失

可以这样操作

  1. 暂时关闭持久化错误导致的写入停止
    通过运行 config set stop-writes-on-bgsave-error no 命令,你可以暂时让 Redis 在 BGSAVE 出错时仍然接受写命令。这样做可以临时保证 Redis 的可用性,防止因为持久化问题而无法对外提供服务。
127.0.0.1:6379>config set stop-writes-on-bgsave-error no
  1. 开发添加key过期时间(这个不急,问题解决后再添加也可,并且开发也需要修改代码)

这里以java代码为例
在 Java 中,你可以使用 Jedis 或 Lettuce 这样的 Redis 客户端库来与 Redis 服务器进行交互并设置键的过期时间。以下是如何使用 Jedis 库在 Java 中设置过期时间的示例:

使用 Jedis 设置键的过期时间

import redis.clients.jedis.Jedis;  

public class RedisExample {  
    public static void main(String[] args) {  
        // 连接到 Redis 服务器  
        Jedis jedis = new Jedis("localhost", 6379);  

        try {  
            // 设置键 "mykey" 的值为 "myvalue",并设置过期时间为 1 小时(3600 秒)  
            jedis.setex("mykey", 3600, "myvalue");  
        } finally {  
            // 最后不要忘记关闭连接  
            jedis.close();  
        }  
    }  
}

在上面的代码中,我们创建了一个 Jedis 实例来连接到本地运行的 Redis 服务器。然后我们使用 setex 方法来设置一个键,该方法接受三个参数:键的名称、过期时间(以秒为单位)和键的值。在这个例子中,键 “mykey” 会在 3600 秒(即 1 小时)后过期。

使用 Lettuce 设置键的过期时间
Lettuce 是另一个流行的 Java Redis 客户端,支持同步、异步和反应式模式。以下是使用 Lettuce 设置键过期时间的示例:

import io.lettuce.core.RedisClient;  
import io.lettuce.core.api.StatefulRedisConnection;  
import io.lettuce.core.api.sync.RedisCommands;  

public class RedisExample {  
    public static void main(String[] args) {  
        // 创建 RedisClient 实例并连接到 Redis 服务器  
        RedisClient redisClient = RedisClient.create("redis://localhost:6379");  
        StatefulRedisConnection<String, String> connection = redisClient.connect();  

        try {  
            // 获取同步命令接口  
            RedisCommands<String, String> syncCommands = connection.sync();  

            // 使用 set 命令并通过 SetArgs 设置过期时间  
            syncCommands.set("mykey", "myvalue", SetArgs.Builder.ex(3600));  
        } finally {  
            // 关闭连接和客户端  
            connection.close();  
            redisClient.shutdown();  
        }  
    }  
}

在上面的代码中,我们创建了一个 RedisClient 实例来连接到 Redis 服务器,然后通过 connect 方法创建了一个连接。接着,我们获取了同步的命令接口 RedisCommands,并使用 set 方法及 SetArgs.Builder.ex 来设置键 “mykey” 的值和过期时间。

无论使用哪个客户端,重要的是确保在设置键时指定过期时间,这样这些键就可以根据 maxmemory-policy volatile-lru 策略在内存达到限制时被适当地淘汰

  1. 设置内存上限和淘汰策略
    然后,你可以通过 config set maxmemory 4gb 和 config set maxmemory-policy volatile-lru 来设置内存使用上限和内存淘汰策略。这将帮助控制内存使用,并在达到内存上限时按照 LRU 算法淘汰已设置过期时间的键。
127.0.0.1:6379>config set maxmemory 4gb
127.0.0.1:6379>config set maxmemory-policy volatile-lru 

#上面是临时配置,重启redis-server服务后失效,同时配置永久生效配置
vim redis.conf
config set maxmemory 4gb
config set maxmemory-policy volatile-lru 
  1. 监控和持久化
    在这些设置之后,你应该监控 Redis 的内存使用情况和持久化状态。如果内存使用接近上限,根据设定的策略,Redis 将开始淘汰键以释放内存。

  2. 持久化成功后重新启动
    一旦你确保了持久化成功(即 BGSAVE 没有错误),你可以计划一个服务窗口来重启 Redis,以便恢复所有的正常配置,包括将 stop-writes-on-bgsave-error 设置回 yes,以确保数据的完整性。

127.0.0.1:6379>bgsave
127.0.0.1:6379>shutdown
./redis-server redis.conf //启动
  1. 内核参数调整

vm.overcommit_memory 是 Linux 系统的内核参数,用于配置内存超额分配的行为。这个参数可以在 /etc/sysctl.conf 配置文件中设置,也可以使用 sysctl 命令临时更改。配置这个参数对 Redis 特别重要,因为 Redis 使用了内存映射文件作为持久化数据的一部分。

vm.overcommit_memory 参数有三个可设置的值:

  1. vm.overcommit_memory = 0: 这是默认设置。在这种模式下,内核会采用启发式算法决定是否允许内存超额分配,这种算法不会考虑分配的内存是否真的会被使用。

  2. vm.overcommit_memory = 1: 在这种模式下,内核允许所有的内存超额分配请求,这对 Redis 来说通常是推荐的设置,因为 Redis 使用了一种叫做“写时复制”(copy-on-write) 的技术在进行持久化操作时。如果不设置为1,Redis 在使用 fork() 创建子进程进行持久化时可能会因为系统报告内存不足而失败,即使物理内存还有足够空间。

  3. vm.overcommit_memory = 2: 在这种模式下,内核将严格限制内存分配,总分配内存不能超过 overcommit_ratio 设置的比例加上所有物理内存的大小。这种设置在内存被高度使用的系统上可能会导致分配失败。

对于运行 Redis 的服务器,通常建议设置 vm.overcommit_memory 为 1,以确保在使用 fork() 进行 RDB 或 AOF 持久化时,即使实际物理内存未被完全使用,也不会因为超额分配的内存检查而失败。

sysctl vm.overcommit_memory=1 //临时生效
永久生效
vim /etc/sysctl.conf
vm.overcommit_memory=1
sysctl -p //加载新配置
sysctl -a //列出所有内核参数

7.建立虚拟内存(swap交换分区)
创建一个 16GB 的 swap 分区并永久挂载

##创建分区文件
sudo dd if=/dev/zero of=/swapfile bs=1G count=16  
sudo chmod 600 /swapfile  
sudo mkswap /swapfile  
##启用swap文件,下面写到fstab无需mount
sudo swapon /swapfile  

##永久挂载
sudo vim /etc/fstab  
/swapfile none swap sw 0 0  

调整 swappiness 参数
swappiness 参数控制 Linux 内核使用 swap 的倾向性。它的值可以是 0 到 100,值越大,内核就越倾向于使用 swap 空间。

如果你想让系统在物理内存消耗完后才使用 swap,你可以将 swappiness 的值设置得较低。例如:

sudo sysctl vm.swappiness=10  //临时生效
vim /etc/sysctl.conf
vm.swappiness = 10    //永久生效
sysctl -p
sysctl -a|grep swappiness

请注意,对于运行数据库或需要快速响应的应用程序,建议不要过度依赖 swap,因为 swap 的速度会比物理内存慢得多。对于 Redis 这类内存数据库,最好是确保有足够的物理内存来支持数据集,以维持最佳性能

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜小徐呐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值