java日志中报错
redis.clients.jedis.exceptions.JedisDataException: 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.
at redis.clients.jedis.Protocol.processError(Protocol.java:66) ~[jedis-2.2.1.jar:na]
at redis.clients.jedis.Protocol.process(Protocol.java:73) ~[jedis-2.2.1.jar:na]
at redis.clients.jedis.Protocol.read(Protocol.java:138) ~[jedis-2.2.1.jar:na]
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:191) ~[jedis-2.2.1.jar:na]
at redis.clients.jedis.Jedis.incr(Jedis.java:560) ~[jedis-2.2.1.jar:na]
redis日志中报错1
Can't save in background: fork: Resource temporaily unavailable
redis日志中报错2
Can’t save in background: fork: Cannot allocate memory
这个是因为,redis有个默认的选项:
stop-writes-on-bgsave-error yes
这个选项默认情况下,如果在RDB snapshots持久化过程中出现问题,设置该参数后,Redis是不允许用户进行任何更新操作。
不彻底的解决方式是,将这个选项改为false
不彻底的解决方式是,将这个选项改为false
stop-writes-on-bgsave-error false
但是这样只是当redis写硬盘快照出错时,可以让用户继续做更新操作,但是写硬盘仍然是失败的;
彻底的解决方式
编辑文件 /etc/sysctl.conf 添加:
vm.overcommit_memory=1
执行sysctl -p使其生效;
vm.overcommit_memory 这个参数又是干什么的呢?
Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存,将这些不会使用的空闲内存分配给其它程序使用,以提高内存利用率,这种技术叫做Overcommit。一般情况下,当所有程序都不会用到自己申请的所有内存时,系统不会出问题,但是如果程序随着运行,需要的内存越来越大,在自己申请的大小范围内,不断占用更多内存,直到超出物理内存,当linux发现内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程,哪些占用内存越多,运行时间越短的进程越有可能被杀掉),以便释放内存。
当oom-killer发生时,linux会选择杀死哪些进程?选择进程的函数是oom_badness函数(在mm/oom_kill.c中),该函数会计算每个进程的点数(0~1000)。点数越高,这个进程越有可能被杀死。每个进程的点数跟(/proc/<pid>/oom_adj)oom_score_adj有关,而且oom_score_adj可以被设置(-1000最低,1000最高)。
当发生oom killer时,会将记录在系统日志中/var/log/messages
Out of memory: Kill process 9682 (mysqld) score 9 or sacrifice child
Killed process 9682, UID 27, (mysqld) total-vm:47388kB, anon-rss:3744kB, file-rss:80kB
httpd invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
Killed process 9682, UID 27, (mysqld) total-vm:47388kB, anon-rss:3744kB, file-rss:80kB
httpd invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
在什么条件下,linux会发现内存不足呢?
在Linux下有个CommitLimit 用于限制系统应用使用的内存资源:
[root@iZuf6c2qeenlv7dqgioq2eZ bin]# grep -i commit /proc/meminfo
CommitLimit: 961120 kB
Committed_AS: 1514856 kB
其中:
CommitLimit是一个内存分配上限,
Committed_AS是已经分配的内存大小。
虚拟内存算法:
CommitLimit = 物理内存 * overcommit_ratio(/proc/sys/vm/overcmmit_ratio,默认50,即50%) + swap大小
它是由内核参数overcommit_ratio的控制的,当我们的应用申请内存的时候,当出现以下情况:
应用程序要申请的内存 + 系统已经分配的内存(也就是Committed_AS)> CommitLimit
这时候就是内存不足,到了这里,操作系统要怎么办,就要祭出我们的主角“overcommit_memory”参数了(/proc/sys/vm/overcommit_memory);
- vm.overcommit_memory = 0 启发策略
- vm.overcommit_memory = 1 允许overcommit
- vm.overcommit_memory = 2 禁止overcommit
前面讲了一大堆参数,那么这些参数又是怎么影响redis的呢?
Redis的数据回写机制分同步和异步两种,
- 同步回写即SAVE命令,主进程直接向磁盘回写数据。在数据大的情况下会导致系统假死很长时间,所以一般不是推荐的。
- 异步回写即BGSAVE命令,主进程fork后,复制自身并通过这个新的进程回写磁盘,回写结束后新进程自行关闭。由于这样做不需要主进程阻塞,系统不会假死,一般默认会采用这个方法。
设置一个合理的写磁盘策略,否则写频繁的应用,也会导致频繁的fork操作,对于占用了大内存的redis来说,fork消耗资源的代价是很大的;