Redis报错MISCONF Redis is configured to save RDB snapshots的解决方法(stop-writes-on-bgsave-error、fork失败与目录权限排查)

业务写Redis时整片失败,报错原文长这样:

(error) MISCONF Redis is configured to save RDB snapshots, but it is currently unable to persist to disk. Commands that may modify the data set are disabled. Please check the Redis logs for details about the RDB error.

读命令照常,写命令全被拒。这个报错不是说磁盘现在已经写满了,而是上一次后台快照失败过,Redis把失败状态记住了。stop-writes-on-bgsave-error默认值是yes,只要RDB没落成功,写命令就一直挡着。设计本身没错,宁可不让写也不让数据悄悄丢,但线上命中一次就是写入全挂,监控上看到的是接口批量500。

这个坑踩过不止一次。2026年处理过的两台机器,一台是目录属主错了,一台是fork拿不到内存。下面按排查顺序记一遍。

1、先确认实例确实停在这个状态

redis-cli info persistence

重点看四行:rdb_last_bgsave_status是不是err,rdb_last_save_time停在什么时候,rdb_bgsave_in_progress是不是0,aof_last_write_status有没有跟着一起报错。落盘目录和文件名也顺手取出来,后面查权限要用:

redis-cli config get dir
redis-cli config get dbfilename
redis-cli config get stop-writes-on-bgsave-error

dir那个值记下来,权限类的问题基本都出在它身上。

2、去日志里找真实原因,别猜

日志位置用config get logfile拿,配成空字符串的话得去systemd里翻:

journalctl -u redis-server -n 200 --no-pager

常见的就这三条,看到哪条按哪条修:

Failed opening the RDB file dump.rdb (in server root dir /var/lib/redis) for saving: Permission denied
Can't save in background: fork: Cannot allocate memory
Failed opening the RDB file temp-18342.rdb for saving: No space left on device

日志里只有MISCONF没有前面那行RDB报错的情况也遇到过,多半是loglevel调高了或者日志被轮转截断。把loglevel设成notice再手动bgsave一次就能抓到。

3、Permission denied:目录不存在或属主不对

Redis跑在redis用户下,dir指向的目录必须对这个用户可写。逐段看权限:

namei -l /var/lib/redis
ls -ld /var/lib/redis
ps -o user=,cmd= -C redis-server

目录不存在就mkdir -p之后chown redis:redis /var/lib/redis,权限给750足够。属主看着对却仍然报错,翻systemd单元:

systemctl cat redis-server

ProtectSystem=full会把/usr、/boot、/etc挂成只读,dir配在/opt或者/etc下面必挂;ReadWritePaths里没列dir也会被挡。之前碰到过一次,redis.conf里改了dir却没在systemd单元里同步ReadWritePaths,重启之后立刻复现。

4、fork: Cannot allocate memory:不是内存不够,是内核不给

这条最容易误判。bgsave靠fork子进程做,copy-on-write理论上不复制内存,但内核在vm.overcommit_memory=0(启发式)下要按最坏情况估算,实例占用一大,fork直接被拒。机器上明明还剩十几个G,照样报这个错。

cat /proc/sys/vm/overcommit_memory
sysctl -w vm.overcommit_memory=1

改完写进/etc/sysctl.conf再sysctl -p,不然重启回退。实测在32G内存、实例占了20G的机器上,改之前bgsave十次失败十次,改完一次就过。

5、容器里的两个额外坑

官方镜像Redis 7.0、7.2、7.4,默认的dir都是/data。/data没挂volume时,RDB写进容器可写层,重启就没;挂了宿主机目录而uid对不上,直接Permission denied。Docker 24.0、25.0,默认的seccomp profile不挡fork,但AppArmor在Ubuntu 22.04、24.04,这类较新的发行版上会挡,日志表现同样是fork失败,free看着却很宽裕。

还有一种是SELinux。之前碰上过,Pod里报Permission denied,宿主机上ls看着权限全对,查了半天是目录的安全上下文不对,audit.log里avc: denied一条条都写着:

ausearch -m avc -ts recent | grep redis
chcon -Rt container_file_t /data/redis

6、先恢复写入,再修根因

redis-cli config set stop-writes-on-bgsave-error no

这一句只是止血,让业务先能写。根因修完手动触发一次:

redis-cli bgsave
redis-cli info persistence | grep rdb_last_bgsave_status

只要有一次bgsave成功,rdb_last_bgsave_status变回ok,写保护自动解除,不用把stop-writes-on-bgsave-error一直留着no——留着等于把持久化失败这件事彻底藏起来,真丢数据的时候连个信号都没有。

config set只改运行时,重启就丢。要固化就改redis.conf里那一行,或者用CONFIG REWRITE写回配置文件(要求dir可写)。

7、顺手看一眼AOF

开了appendonly yes的实例,AOF写失败走的是另一条路,报错是Can't open the append-only file ... Permission denied。Redis 7.0之后AOF拆成了appenddirname那个多文件结构,权限要连目录一起给。前面说的两台机器里有一台就是RDB修好了、appendonlydir属主还是root,重启后又挂。

哨兵和集群环境每个节点都要单独查,从节点一样会停写。数据截至2026年9月,Redis 7.4的默认配置项以官方redis.conf为准。