今天在 redis 中执行 setrange name 1 chun 命令时报了如下错误提示:
(error) 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.
这是由于强制停止 redis 快照,不能持久化引起的,运行 info 命令查看 redis 快照的状态,如下:
解决方案如下:
连接 redis 后运行 config set stop-writes-on-bgsave-error no
命令
关闭配置项 stop-writes-on-bgsave-error 解决该问题。
今天第二次遇到 Redis“MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk”的问题。这个错误信息是 Redis 客户端工具在保存数据时候抛出的异常信息。
网上查了一下,很多人都是建议“config set stop-writes-on-bgsave-error no”。这样做其实是不好的,这仅仅是让程序忽略了这个异常,使得程序能够继续往下运行,但实际上数据还是会存储到硬盘失败!
上一次遇到这个问题是因为一个程序的 Bug 造成系统内存被耗尽了,后来修复了那个 Bug 问题就解决了。今天出现问题时查看系统内存还有 2GB 左右,“感觉好像不是内存的缘故”(后面发现还是因为内存的缘故)。
由于 Redis 是 daemon 模式运行的,没法看到详细的日志。修改配置文件设置 logfile 参数为文件(默认是 stdout,建议以后安装完毕就修改这个参数为文件,不然会丢掉很多重要信息),重启 Redis,查看日志,看到程序启动时就有一行警告提示:
“WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add‘vm.overcommit_memory = 1’to /etc/sysctl.conf and then reboot or run the command‘sysctl vm.overcommit_memory=1’for this to take effect.”(警告:过量使用内存设置为 0!在低内存环境下,后台保存可能失败。为了修正这个问题,请在 /etc/sysctl.conf 添加一项‘vm.overcommit_memory = 1’,然后重启(或者运行命令’sysctl vm.overcommit_memory=1’)使其生效。)
当时没明白意思,就忽略了。再启动 Redis 客户端,程序保存数据时继续报“MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk”异常,再查看 Redis 日志,看到有这样的错误提示“Can’t save in background: fork: Cannot allocate memory”,这个提示很明显 "Fork 进程时内存不够用了!"(还是内存的问题)。
通过谷歌查询“Can’t save in background: fork: Cannot allocate memory”这个提示,找到了解决方法:
修改 /etc/sysctl.cnf
文件修改为vm.overcommit_memory=1
Redis 的数据回写机制分同步和异步两种,
- 同步回写即 SAVE 命令,主进程直接向磁盘回写数据。在数据大的情况下会导致系统假死很长时间,所以一般不是推荐的。
- 异步回写即 BGSAVE 命令,主进程 fork 后,复制自身并通过这个新的进程回写磁盘,回写结束后新进程自行关闭。由于这样做不需要主进程阻塞,系统不会假死,一般默认会采用这个方法。
个人感觉方法2采用 fork 主进程的方式很拙劣,但似乎是唯一的方法。内存中的热数据随时可能修改,要在磁盘上保存某个时间的内存镜像必须要冻结。冻结就会导致假死。fork 一个新的进程之后等于复制了当时的一个内存镜像,这样主进程上就不需要冻结,只要子进程上操作就可以了。
在小内存的进程上做一个 fork, 不需要太多资源,但当这个进程的内存空间以G为单位时,fork 就成为一件很恐怖的操作。何况在 16G 内存的主机上 fork 14G 内存的进程呢?肯定会报内存无法分配的。更可气的是,越是改动频繁的主机上 fork 也越频繁,fork 操作本身的代价恐怕也不会比假死好多少。
找到原因之后,直接修改内核参数 vm.overcommit_memory = 1
Linux 内核会根据参数 vm.overcommit_memory 参数的设置决定是否放行。
- 如果 vm.overcommit_memory = 1,直接放行
- vm.overcommit_memory = 0:则比较 此次请求分配的虚拟内存大小和系统当前空闲的物理内存加上 swap,决定是否放行。
- vm.overcommit_memory = 2:则会比较 进程所有已分配的虚拟内存加上此次请求分配的虚拟内存和系统当前的空闲物理内存加上 swap,决定是否放行。
微信扫描下方的二维码阅读本文