十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Redis 报错 “MISCONF Redis is configured to save RDB snapshots“?一文讲透根因与根除方案

Redis 报错 “MISCONF Redis is configured to save RDB snapshots“?一文讲透根因与根除方案 Redis 报错 “MISCONF Redis is configured to save RDB snapshots”?一文讲透根因与根除方案一、事故现场一个风平浪静的下午,Java 应用突然大面积报错:org.springframework.dao.InvalidDataAccessApiUsageException: MISCONF Redis is configured to save RDB snapshots, but its currently unable to persist to disk. Commands that may modify the data set are disabled...更离谱的是,报错信息里挂的命令竟然是(PING)——连 Redis 的心跳检测都失败了。缓存读写全挂,业务接口一片红。别慌,这个报错其实非常诚实,它把病因直接写在了脸上。下面带你 5 分钟看懂、10 分钟根治。事故时间线还原T-2h Doris 容器日志疯狂增长磁盘使用率 85% → 95% 无人察觉因为没有告警 T-0 磁盘 100%Redis 到点执行 bgsave写入失败 T1s Redis 触发 stop-writes-on-bgsave-error进入拒绝写模式 T3s 应用侧 Redisson 健康检查 PING 带出服务端错误异常爆发 T40m 排查清理 20G 日志 → bgsave 成功 → 业务自动恢复报错触发机制为什么会有 MISCONF成功失败yes默认noRedis 按 save 规则触发 bgsavefork 子进程写 RDB 到磁盘写盘成功?一切正常检查 stop-writes-on-bgsave-error 锁定写操作抛出 MISCONF⚠️ 写操作放行但数据不落盘宕机即丢失修复根因后bgsave 成功二、报错翻译:Redis 到底在说什么?把这段英文翻译成人话:“我(Redis)配置了定期保存 RDB 快照,但我现在没法往磁盘写文件了。为了防止’内存里的数据’和’磁盘上的快照’越差越远,我拒绝执行一切写命令,直到我能正常存盘为止。”这里涉及两个关键配置:配置作用默认值save 秒 变更次数触发 RDB 快照的条件,如save 900 1开启stop-writes-on-bgsave-errorbgsave 失败时是否拒绝写入yes打个比方:Redis 是个仓库管理员,按规定要定期把库存盘点存档(RDB 快照)。某天他发现档案柜塞满了,存不进去——为了账实相符,他直接锁上大门:“存档恢复之前,谁也别进货(写数据)!”至于为什么连PING都报错:PING 本身不修改数据,但你的应用(Redisson)在连接健康检查时会带出服务端的错误状态,异常顺着调用链传播了出来。报错落在哪条命令上不重要,病根 100% 在 Redis 服务端。 顺带一提此状态下 GET 等读命令仍然正常只有写命令被拒。这也是为什么监控里Redis 内存占用还很高、读请求没全挂反而迷惑性更强排障决策流程图核心✅ 是90% 案例❌ 否No space left on deviceCannot allocate memoryPermission deniedRead-only file system✅ ok❌ 仍失败 应用报 MISCONF 错误第1步df -h磁盘满了?Use% ≥ 100找到吃盘大户du truncate 清理第2步看 Redis 日志docker logs redis日志报什么错?磁盘满df 与 du 不一致检查已删除但占用的文件内存 fork 失败设置 overcommit_memory1权限不足chown redis 数据目录磁盘故障修复文件系统第3步确认状态redis-cli info persistencerdb_last_bgsave_status err根因已确认修复根因清盘 / 调参数 / 改权限第4步解除锁定redis-cli bgsavebgsave 成功? 写锁定自动解除业务恢复正常回到第2步根因没修干净第5步加固防复发Docker日志限额 磁盘80%告警三、三板斧定位:先查磁盘!第一板斧:看磁盘(90% 的元凶就是它)df-h只要看到 Redis 数据目录所在分区Use% 100%,恭喜,破案了。我的真实案例:一台服务器根分区只有 49G,同机的 Doris 数据库容器日志疯狂输出(每秒刷 INFO),Docker 的 json 日志文件涨到 20G,直接把磁盘吃干抹净——Redis 成了无辜的连带受害者。⚠️ 还有一个隐蔽变体磁盘空间没满但 inode 耗尽了——大量小文件session 文件、temporary 文件吃光 inode同样会导致写文件失败。补一条命令df-i# 看 IUse% 是否 100%第二板斧:看 Redis 日志# Docker 部署dockerlogs redis--tail50# 传统部署tail-50/var/log/redis/redis-server.log找这几行:Failed saving the DB: Permission denied # 权限问题 Cant save in background: fork: Cannot allocate memory # 内存问题 Write error saving DB on disk: No space left on device # 磁盘满(实锤)第三板斧:问 Redis 本人redis-cli info persistence|greprdb重点看:rdb_bgsave_in_progress:0 rdb_last_bgsave_status:err ← 上次快照失败,实锤 rdb_last_save_time:1722600000 rdb_changes_since_last_save:18342 ← 已有 1.8 万条变更没存盘rdb_last_bgsave_status:err就是 Redis 锁门的直接原因。四、三大根因对照表附原理根因日志特征原理一句话高发场景磁盘满 / inode 满No space left on device快照文件写不进去日志/容器/业务数据把盘吃光最常见fork 失败Cannot allocate memory见下方原理展开物理内存紧张或大内存 Redis目录无权限Permission denieddir目录 Redis 用户没有写权限改过数据目录、跑过 sudo 启动等SELinux 拦截Permission denied但权限看起来正常SELinux 策略阻断写路径CentOS/RHEL 默认 enforcing 环境重点展开fork 失败为什么这么常见bgsave 依赖fork()创建子进程来落盘。fork 使用写时复制Copy-on-Write父子进程一开始共享物理内存页只有父进程修改数据时才复制。但为了安全起见Linux 在 fork 时需要确认如果所有页都要复制内存够不够。当vm.overcommit_memory0默认启发式策略时一个 16G 的 Redis 在剩余内存不足的机器上 fork直接失败。这就是为什么很多中大型实例的 Redis 文档都要求sysctlvm.overcommit_memory1echovm.overcommit_memory1/etc/sysctl.conf# 永久生效这也是 Redis 启动日志里那句著名警告的由来WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.SELinux 排查容易被忽略getenforce# Enforcing 则在拦截范围内ausearch-mavc-tsrecent# 查看最近的拦截记录# 临时验证setenforce 0 后 bgsave 成功 → 说明是 SELinux 的锅五、恢复操作:顺序很重要!⚠️ 错误姿势:一上来就重启 Redis 或关闭保护开关——根因没解决,几分钟后必然复发。重启甚至还会因为加载旧 RDB 丢掉最近的变更。正确姿势分两步:第 1 步:先治根因(以磁盘满为例)# 揪出谁在吃磁盘(按大小排序)du-h--max-depth1/2/dev/null|sort-hr|head-10# Docker 用户重点排查容器日志du-h/var/lib/docker/containers/*/*-json.log2/dev/null|sort-hr|head-5清理出空间(本次案例:清掉 20G 的 Doris 容器日志,并给容器加日志限额防止再犯)。第 2 步:让 Redis 自己解除锁定# 手动触发一次快照redis-cli bgsave# 确认成功redis-cli info persistence|greprdb_last_bgsave_status# 显示 rdb_last_bgsave_status:ok → 写锁定自动解除,业务恢复应急方案(实在没法立刻清磁盘时):redis-cli configsetstop-writes-on-bgsave-error no这能让写命令立即恢复但请务必清楚代价满足以下条件再用✅ 该实例是纯缓存数据可重建不怕丢✅ 已确认根因是磁盘问题且修复排期明确❌ Redis 承担持久化职责如排行榜、计数器唯一存储→绝对不要关记住这只是拆掉保险丝让设备强行运转RDB 依旧存不了宕机即丢数据。用完务必改回yes并且注意config set是运行时修改重启会失效——好消息恢复默认保护和坏消息你以为关了其实又开了都在这里。六、根除与预防清单1. 给 Docker 日志戴上紧箍咒(本次事故的真正元凶)/etc/docker/daemon.json加全局配置:{log-driver:json-file,log-opts:{max-size:50m,max-file:3}}单个容器最多 150MB 日志,神仙也写不爆磁盘。改完重启 docker 生效。注意此配置只对新创建的容器生效老容器需要重建个别话特别多的容器可以单独配置log-opt覆盖。2. Redis 自身加固# 数据目录指向大盘(redis.conf)dir/data/redis# 纯缓存场景可以放宽快照频率,甚至关闭 RDBsave36001# 或彻底关闭:save # 解决 fork 失败(需 root)sysctlvm.overcommit_memory1# 确认保护开关是默认状态stop-writes-on-bgsave-erroryes3. 监控告警双保险磁盘层面使用率超 80% 告警inode 超 80% 也告警别等 100% 让 Redis 替你报警——它报警的方式可是拉全站陪葬。Redis 层面用监控工具Prometheus redis_exporter / 云 Redis 自带监控盯rdb_last_bgsave_status等于在 Redis锁门之前就收到通知——出问题的是 bgsave而不是写命令本身。**4. 别关stop-writes-on-bgsave-error它是保险丝不是毛病很多人一百度就找到config set stop-writes-on-bgsave-error no这个万能解直接写进了配置——这等于给核电站报警器插了耳塞。这个开关的价值在于用拒绝写入这种最疼的方式逼你正视持久化失败避免了内存数据很丰满、磁盘快照很骨感的惨剧。试想如果默认关闭磁盘早已满了三个月Redis 岁月静好某天机器意外断电——重启后数据直接回到三个月前的快照为时已晚。真正该修的是让这个开关永远没有触发的机会。** 5. 架构层面的终极保障**如果 Redis 承载的是关键业务单机无论如何加固都有孤立无援的风险建议主从 哨兵bgsave 放在从节点做主节点不扛 fork 压力主库磁盘挂了还能自动切换。监控 Redis 自身指标重点盯rdb_last_bgsave_status、used_memory_rssfork 时内存膨胀预警、aof_last_write_status接 Prometheus 告警。Docker 数据卷独立分区把/var/lib/docker和 Redis 数据目录放到独立大盘与应用日志物理隔离避免一损俱损。七、速查表建议收藏步骤命令看什么① 查磁盘df -h/df -i空间/inode 是否 100%② 查日志docker logs redis --tail 100磁盘/内存/权限/只读四类错误③ 查状态redis-cli info persistencerdb_last_bgsave_status:err④ 治根因du -h --max-depth1 / | sort -hr找到并清理吃盘大户⑤ 解除锁定redis-cli bgsave状态变ok即自动恢复⑥ 应急解锁config set stop-writes-on-bgsave-error no仅限应急用完改回 yes八、一张图记住排查思路Java 报 MISCONF │ ▼ df -h 查磁盘 ──── 满了 → 清盘(du 找大户) → bgsave → 恢复 ✅ │ 没满 ▼ docker logs redis ──── Cannot allocate memory → overcommit_memory1 → bgsave ✅ │ ──── Permission denied → chown redis 数据目录 → bgsave ✅ │ ──── Read-only file system → 检查磁盘健康 → 修复文件系统 ✅写在最后MISCONF是 Redis 最话痨的报错——它把原因、机制、涉及的配置项全写进了报错信息里堪称报错界的模范生。记住排查口诀先查盘、再看日志、修完根因、bgsave 解锁而这次的血泪教训有两个希望你不用自己踩一遍一台服务器上任何一个容器的失控日志都可能让隔壁的 Redis 替你背锅宕机。Docker 日志限额今天就去配上。stop-writes-on-bgsave-error是 Redis 最后的倔强它宁可把自己锁死也不愿让数据账实不符。请尊重这份倔强——别关它去修它报警的原因。
返回列表