Redis 突然写不进去,客户端抛的是 (error) MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk。这跟内存满没关系,也不是集群挂了。Redis 发现自己的 RDB 快照写盘失败,就把所有写命令一刀切掉,免得你在一份根本存不下来的快照上继续改数据。想恢复写入,唯一的路是让一次 BGSAVE 真正成功跑完。
一、先分清:MISCONF 和 OOM 是两码事
两种报错都会让 SET、LPUSH、HSET 全部失败,可根因方向完全相反。看错报错去调 maxmemory,能白折腾半小时。
| 报错原文 | 卡在哪 | 一条命令定性 |
|---|---|---|
MISCONF ... unable to persist to disk |
RDB / AOF 落盘失败 | INFO persistence 里 rdb_last_bgsave_status:err |
OOM command not allowed when used memory > 'maxmemory' |
内存顶到上限 | INFO memory 里 used_memory ≈ maxmemory |
LOADING Redis is loading the dataset in memory |
启动时加载 RDB 中 | 等它加载完,别急着重启 |
还有一个容易踩的细节:MISCONF 只拦「会改数据」的命令。GET、TTL、INFO 照样能用,所以监控面板上看不出异常,业务侧却在成片报 500。很多队列任务(Sidekiq、Celery、Bull 这类)就是被这一条掐死的。
二、三条命令把根因挖出来
不猜,直接问 Redis 自己。
$ redis-cli INFO persistence | grep -E "rdb_last_bgsave_status|rdb_changes_since_last_save|rdb_last_cow_size|aof_last_write_status"
rdb_changes_since_last_save:18422
rdb_last_bgsave_status:err
rdb_last_cow_size:0
aof_last_write_status:ok
rdb_last_bgsave_status:err 一出现,MISCONF 就一定会跟着来。接着确认它到底想往哪儿写:
$ redis-cli CONFIG GET dir
1) "dir"
2) "/var/lib/redis"
$ tail -n 200 /var/log/redis/redis-server.log | grep -iE "saving|fork|write error|permission"
[8842] 29 Sep 2026 09:12:44.031 * Background saving started by pid 8842
[8842] 29 Sep 2026 09:12:46.118 # Write error saving DB on disk: No space left on device
[8842] 29 Sep 2026 09:12:46.120 # Background saving error
日志里那行才是真凶。No space left on device、fork: Cannot allocate memory、Permission denied,三种话对应三种完全不同的修法。
三、根因对号入座
| 日志关键词 | 真实原因 | 修法 |
|---|---|---|
| No space left on device | RDB 目录所在分区余量不够写临时文件 | 清空间,或 CONFIG SET dir /data/redis 换盘 |
| Can’t save in background: fork: Cannot allocate memory | vm.overcommit_memory=0,内核拒绝 fork |
sysctl -w vm.overcommit_memory=1 并写进 sysctl.conf |
| Permission denied / Read-only file system | 目录属主不对,或挂载点掉成只读 | chown -R redis:redis + chmod 755,再查 mount |
| Can’t open the append only file | AOF 目录满了或权限错(看 aof_last_write_status) |
同上,AOF 和 RDB 通常同目录 |
四、五步把写入救回来
或换 dir
BGSAVE
= ok
试通
配置
# 1) 临时把落盘目录挪到有余量的数据盘(重启失效)
$ redis-cli CONFIG SET dir /data/redis
# 2) 手工触发一次全量快照
$ redis-cli BGSAVE
Background saving started
# 3) 等两秒,确认这次真成功了
$ redis-cli INFO persistence | grep rdb_last_bgsave_status
rdb_last_bgsave_status:ok
# 4) 写命令恢复,MISCONF 会自动消失
$ redis-cli SET health:check 1
OK
注意第 4 步不用重启 Redis。MISCONF 是个「状态标记」,只要下一次 BGSAVE 成功,它自己就解除了。重启反而会把内存数据全丢一遍。
踩坑实录
一台 8G 内存的缓存机,df -h 显示根分区用了 92%,dump.rdb 已经 3.1G。我当时想的是「又没到 100%,能有什么事」,结果 BGSAVE 照样甩 No space left on device。坑在临时文件:快照先写 temp-8842.rdb,写完再 rename 覆盖 dump.rdb,这份临时文件差不多就是整个数据集大小。只剩 300 多 MB 余量,3G 的新快照连头都开不了。把 /var/lib/redis 整体挪到数据盘重跑,状态从 err 直接变 ok。判断 RDB 能不能写,看的不是「满了没有」,而是「余量够不够一份完整快照」。
五、长期配置:四件事
/etc/sysctl.conf 落地rdb_last_bgsave_status 做告警,别等业务报错stop-writes-on-bgsave-error 保持 yes,别图省事改成 noCONFIG SET stop-writes-on-bgsave-error no 放行,那只是把报警器拆了,数据依旧存不下来;只有当这台 Redis 纯做缓存、丢了能重建时,才可以这么干。常见问题
Q1:执行了 CONFIG SET stop-writes-on-bgsave-error no,之后还要改回 yes 吗?
要。它只是绕过检查,RDB 依旧是写失败的。CONFIG SET 改的是运行时配置,重启即失效,但保险做法是修好之后主动 CONFIG SET stop-writes-on-bgsave-error yes,再 CONFIG REWRITE 固化。
Q2:Redis 跑在 Docker 里报 MISCONF,宿主机磁盘明明很空?
宿主机空闲不代表容器能写。CONFIG GET dir 指的往往是容器可写层或某个卷,而卷配额、可写层单独就塞满了。进容器里查:docker exec -it redis sh -c "df -h $(redis-cli CONFIG GET dir | tail -1)"。
Q3:主库报 MISCONF,从库却一切正常,是什么情况?
正常。dir 是每台实例自己的配置,从库写的是它本地的 dump 文件。主库磁盘满、从库磁盘空,就会是这个现象。别主从一起重启,先单独修主库。
如果你也踩过这个坑,或者是在宝塔、云数据库这类面板环境里遇到的(面板常把 dir 指向别的路径,排查思路一样但入口不同),欢迎在下面留言说说你的现场,我会挑典型的补进 fenij.com 的后续文章里。