首页 > Technology > 正文

Redis 写入被拒:MISCONF 排错实录

fenij 2026-09-29 4 Technology

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 通常同目录

四、五步把写入救回来

1
腾出空间
或换 dir

2
手工
BGSAVE

3
确认 status
= ok

4
写命令
试通

5
补长期
配置

# 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 能不能写,看的不是「满了没有」,而是「余量够不够一份完整快照」。

五、长期配置:四件事

🧠
overcommit 置 1
fork 才不会被内核拒绝,写进 /etc/sysctl.conf 落地

💾
余量留 1.5 倍
RDB 目录空闲空间 ≥ 数据集 1.5 倍,别再卡 90% 运行

🔔
监控快照状态
对 rdb_last_bgsave_status 做告警,别等业务报错

🔒
闸门保持关闭
stop-writes-on-bgsave-error 保持 yes,别图省事改成 no

✅ 处置结论
先修磁盘 / 权限 / fork,再靠一次成功的 BGSAVE 自动解锁。不建议直接用 CONFIG 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 的后续文章里。

相关完整手册

系统化的排查与配置思路,建议顺手收藏这几篇完整手册: