最近遇到应用程序客户端在往redis中写入数据的时候发生了一个这样一个错误:
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.
第一次遇到这种错误仍是蛮好奇的,主要是它没法重现(或必现),只是会偶尔出现,客户端抛出该异常后redis服务状态也是正常的,特地查了一下相关的资料,结合当时本身的操做场景,还算是解释的通的。
该问题的大概缘由在于:开启了snapshot的持久化模式,且在大量写入的时候bgsave持久化异常,致使客户端写入数据失败,缘由译文中有说起。
固然解决办法也很简单,设置参数stop-writes-on-bgsave-error为no,也即bgsave异常的时候不要阻止继续写入数据。
git
笔者的环境是:centos 7+ Redis 5.0.1;
操做场景是:并发线程以pipline的方式往redis中写入数据,这样会频繁促使发生后台持久化线程pgsave的工做(操做系统远大于redis中的数据占用内存);
异常现象:客户端会偶发上述错误,可是redis服务自己没有异常,过后能够正常访问。
经检查redis的日志和系统日志,均没有记录到异常信息,应该是redis设置了stop-writes-on-bgsave-error=yes的状况下,服务端bgsave失败以后一种正常的阻止继续写入行为。
同时,尽管在操做系统的内存充足的状况下,redis的bgsave为何会失败,这也是一个须要思考的问题。
这个问题一直没有查到日志(不论是redis的日志仍是系统的日志),一直怀疑是否是本身整错什么东西了,直至发现这个哥们也提到这个问题:the issue happens intermittently and dont know why. no indication in the logs.github
另:redis的bgsave是一个独立于redis服务的进程,对于在大批量写入的状况下,常常能够看到一个redis实例下的这个bgsave独立进程(进程之间是没法共享内存的)redis
如下为译文,原文地址:https://blog.sakuragawa.moe/debugging-misconf-redis-is-configured-to-save-rdb-snapshots/数据库
有时候Redis会抛出以下的错误:centos
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.
发生此错误是由于BGSAVE失败。不少时候BGSAVE失败是由于fork没法分配内存。不少时候fork没法分配内存(尽管机器有足够的可用RAM),由于操做系统的优化冲突。服务器
Override Error in Redis Configuration并发
使用redis cli,您能够中止尝试保存快照:app
config set stop-writes-on-bgsave-error no
这是一个快速的解决方法,可是若是您关心使用它的数据,您应该首先检查一下BGSAVE失败的缘由。
这就暂时解决了这个问题。可是,过分查看这个错误是一种可怕的方法,由于这个选项的做用是阻止redis通知写操做已经中止,而且在不将数据写入快照的状况下继续操做。这只是忽略了这个错误。dom
Save Redis on Low Memoryide
因为内存不足,bgsave过程当中可能出现错误。
发生此错误是由于BGSAVE失败。在BGSAVE期间,Redis fork一个子进程以将数据保存到磁盘上。
虽然BGSAVE失败的确切缘由能够从日志中查看(一般在/var/log/redis/redis-服务器日志可是不少时候BGSAVE失败是由于fork不能分配内存。不少时候fork没法分配内存(尽管机器有足够的可用RAM),由于操做系统的优化冲突。
从Redis常见问题中能够看出:
Background saving is failing with a fork() error under Linux even if I've a lot of free RAM!
Redis后台保存模式依赖于现代操做系统中fork的copy-on-write语义:Redis forks(建立一个子进程)是父进程的彻底副本。子进程将数据库转储到磁盘上,最后退出。
理论上,子进程应该使用与父进程做为副本同样多的内存,但实际上因为大多数现代操做系统实现的“写时拷贝”语义,父进程和子进程将共享公共内存页。
只有在子级或父级中更改页时,才会复制该页。由于理论上,当子进程保存时,全部的页面均可能会发生变化,因此Linux没法预先知道子进程将占用多少内存,所以,若是overcommit_memory设置设置为zero fork,则将失败,除非有足够多的可用RAM来真正复制全部父内存页,结果是,若是你有一个3gb的Redis数据集,而只有2gb的空闲内存,那么它将失败。
# echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf # sysctl vm.overcommit_memory=1
Redis不须要操做系统认为的那样多的内存来写入磁盘,所以可能会先发制人地让fork失败。