问题描述:

Redis连接不上,但是redis已经启动,程序报错:

【192.168.104.104:6379/0】状态不可用,等待后台检查程序恢复方可使用。
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.
misconf redis被配置以保存数据库快照,但misconf redis目前不能在硬盘上持久化。
用来修改数据集合的命令不能用,请使用日志的错误详细信息。

查看日志 redis.log 错误信息如下:

7225:M 12 Oct 15:43:54.028 # Can’t save in background: fork: Cannot allocate memory
7225:M 12 Oct 15:44:00.006 * 10 changes in 300 seconds. Saving…

查看内存:

Redis启动后剩余内存一致在变化 180~140M 左右
Redis关闭后 内存剩余 500+M
Redis工作至少需要 300+M 内存空间
尝试重启Redis 短暂连接后失败。

问题原因:

由于内存有限,Redis运行Fork进程时内存不够用。
Redis数据回写机制分为同步和异步两种:

  1. 同步写(SAVE)命令,主进程直接向磁盘回写数据,数据量大会导致系统假死很长时间,不推荐。
  2. 异步写(BGSAVE)命令,主进程fork后,复制自身并通过新的进程回写磁盘,回写结束后关闭,不需要主进程阻塞即系统不会假死,Redis默认使用该方法。
    但是另起一个相同进程(Redis运行在内存中),在内存不够下一定会导致内存无法分配问题发生。

解决办法:

方案一:

目前情况无法增加内存,但可以通过修改内核参数来调整内存分配策略来避免报错。

  1. 编辑/etc/sysctl.conf ,改vm.overcommit_memory=1
  2. sysctl -p 使配置生效(永久)

vm.overcommit_memory 内存策略有三种:

0:表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1:表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2:表示内核允许分配超过所有物理内存和交换空间总和的内存。

即使允许分配内存,实际剩余内存还是不够新进程完成写磁盘操作?
Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存。这种技术叫做 Overcommit。
当linux发现内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程),以便释放内存。
当oom-killer发生时,linux会选择杀死哪些进程?选择进程的函数是oom_badness函数(在mm/oom_kill.c中),该函数会计算每个进程的点数(0~1000)。
点数越高,这个进程越有可能被杀死。每个进程的点数跟oom_score_adj有关,而且 oom_score_adj可以被设置(-1000最低,1000最高)。

简而言之Linux会自动释放一些内存空间使申请内存的进程运行。

方案二:

限制Redis最大使用的内存Redis配置中有一个 “maxmemory” 在生成快照期间,Redis内存使用量会为 2*maxmemory + Redis占用内存。
具体情况如何,有待测试。