前些天一直受到内存报警,过一段时间就会恢复。由于开发工作有些多,就一直没理它,但是最近几天开始有些频繁了。虽然不影响业务,但是天天报警,还是让人提心吊胆的。因此就抽了一个上午的时间去解决一下这个问题。

排查问题

这台机器安装的是mongodb,因为最近业务增加,内容使用增加是正常的,但是实际的占用内存,也不能达到80%的使用率。

首先,使用free -m 命令 查看一下内存的使用情况,这里简单介绍一下这个命令的返回

total:内存总数
used:已经使用的内存数
free:空闲的内存数
shared:多个进程共享的内存总额
buff/cache:磁盘缓存的大小

发现buff/cache 占用的过高。

其次又分析了一下mongoDB 目前数据占用的空间,是正常的。

分析问题

先 了解一下什么是buff/cache?

Buffer cache则主要是设计用来在系统对块设备进行读写的时候,对块进行数据缓存的系统来使用。比如我们在格式化文件系统的时候。

一般情况下两个缓存系统是一起配合使用的,比如当我们对一个文件进行写操作的时候,page cache的内容会被改变,而buffer cache则可以用来将page标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。这样,内核在后续执行脏数据的回写(writeback)时,就不用将整个page写回,而只需要写回修改的部分即可。

Linux内核会在内存将要耗尽的时候,触发内存回收的工作,以便释放出内存给急需内存的进程使用。

既然它主要用来做缓存,只是在内存够用的时候加快进程对文件的读写速度,那么在内存压力较大的情况下,当然有必要清空释放cache,作为free空间分给相关进程使用。所以一般情况下,我们认为buffer/cache空间可以被释放,这个理解是正确的。

但是这种清缓存的工作也并不是没有成本。所以伴随着cache清除的行为的,一般都是系统IO飙高。因为内核要对比cache中的数据和对应硬盘文件上的数据是否一致,如果不一致需要写回,之后才能回收。

怎么解决问题?

怎样释放Buff/cache呢?

它是通过proc下的一个文件释放,/proc是一个虚拟文件系统,我们可以通过对它的读写操作做为与kernel实体间进行通信的一种手段.也就是说可以通过修改/proc中的文件,来对当前kernel的行为做出调整.那么我们可以通过调整/proc/sys/vm/drop_caches来释放内存。

具体操作命令如下:

sync
echo 1 > /proc/sys/vm/drop_caches
echo 2 > /proc/sys/vm/drop_caches
echo 3 > /proc/sys/vm/drop_caches

sync 命令将所有未写的系统缓冲区写到磁盘中,包含已修改的 i-node、已延迟的块 I/O 和读写映射文件。切记释放前最好sync一下,防止丢数据。

但是等到每次报警再去释放,不仅不及时,也不方便,所以我在网上也参考一些文章,写了一个定时释放内存的脚本freemem.sh ,脚本内容如下:

#! /bin/bash
# 需要释放内存的,内存使用百分比,可以传参,默认是85%
max_rate=$1
if [ ! "$max_rate" ] ; then
	max_rate=85
fi
echo "max_rate: $max_rate"

total=free -m | awk <span class="hljs-string">'NR==2'</span> | awk <span class="hljs-string">'{print $2}'</span>
used=free -m | awk <span class="hljs-string">'NR==2'</span> | awk <span class="hljs-string">'{print $3}'</span>
free=free -m | awk <span class="hljs-string">'NR==2'</span> | awk <span class="hljs-string">'{print $4}'</span>
rate=\(((<span class="hljs-variable">\)used*100/\(total</span>)); <span class="hljs-built_in">log</span>=/www/wwwlogs/mem.log <span class="hljs-built_in">echo</span> <span class="hljs-string">"==========================="</span> &gt;&gt; <span class="hljs-variable">\)log
date >> \(log</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"current_rate: <span class="hljs-variable">\)rate"
echo "Memory usage | [Total:\({total}</span>MB][Use:<span class="hljs-variable">\){used}MB][Free:\({free}</span>MB]"</span> &gt;&gt; <span class="hljs-variable">\)log
if [ "\(rate</span>"</span> -ge <span class="hljs-string">"<span class="hljs-variable">\)max_rate" ] ; then
sync && echo 1 > /proc/sys/vm/drop_caches
sync && echo 2 > /proc/sys/vm/drop_caches
sync && echo 3 > /proc/sys/vm/drop_caches
echo "OK" >> \(log</span> <span class="hljs-keyword">else</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"Not required"</span> &gt;&gt; <span class="hljs-variable">\)log
fi

脚本有了,需要把脚本配置到crontab中(或者其他的定时任务平台)。

crontab配置如下,每半个小时执行一次:

*/30 * * * * root /root/freemem.sh

大家在使用过程中有什么问题,或者建议,可以给我留言。

其他的解决方法

通过这个问题对这个buff/cache 有了了解,我就思考:

linux系统应该可以配置不使用buff/cache吧?

buff/cache 的系统自动回收,是不是也是可以配置自动回收的上限呢?

等等问题。我就找到了以下内容:

修改/etc/sysctl.conf 添加如下选项后就不会内存持续增加

vm.dirty_ratio = 1
vm.dirty_background_ratio=1
vm.dirty_writeback_centisecs=2
vm.dirty_expire_centisecs=3
vm.drop_caches=3
vm.swappiness =100
vm.vfs_cache_pressure=163
vm.overcommit_memory=2
vm.lowmem_reserve_ratio=32 32 8
kern.maxvnodes=3

上面的设置比较粗暴,使cache的作用基本无法发挥。

介绍一下具体的配置项:

dirty_ratio

这个参数控制文件系统的文件系统写缓冲区的大小,单位是百分比,表示系统内存的百分比,表示当写缓冲使用到系统内存多少的时候,开始向磁盘写出数 据。增大之会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能。但是,当你需要持续、恒定的写入场合时,应该降低其数值,一般启动上缺省是 10。设1加速程序速度;

dirty_background_ratio

这个参数控制文件系统的pdflush进程,在何时刷新磁盘。单位是百分比,表示系统内存的百分比,意思是当写缓冲使用到系统内存多少的时 候,pdflush开始向磁盘写出数据。增大之会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能。但是,当你需要持续、恒定的写入场合时, 应该降低其数值,一般启动上缺省是 5;

dirty_writeback_centisecs

这个参数控制内核的脏数据刷新进程pdflush的运行间隔。单位是 1/100 秒。缺省数值是500,也就是 5 秒。如果你的系统是持续地写入动作,那么实际上还是降低这个数值比较好,这样可以把尖峰的写操作削平成多次写操作;

dirty_expire_centisecs

这个参数声明Linux内核写缓冲区里面的数据多“旧”了之后,pdflush进程就开始考虑写到磁盘中去。单位是 1/100秒。缺省是 30000,也就是 30 秒的数据就算旧了,将会刷新磁盘。对于特别重载的写操作来说,这个值适当缩小也是好的,但也不能缩小太多,因为缩小太多也会导致IO提高太快。建议设置为 1500,也就是15秒算旧。 

drop_caches

释放已经使用的cache;

page-cluster

该文件表示在写一次到swap区的时候写入的页面数量,0表示1页,1表示2页,2表示4页。

swapiness

该文件表示系统进行交换行为的程度,数值(0-100)越高,越可能发生磁盘交换。

vfs_cache_pressure

该文件表示内核回收用于directory和inode cache内存的倾向

vm.dirty_ratio = 5    #dft 20  %
vm.dirty_background_ratio =5 #dft 10 %
vm.dirty_writeback_centisecs=100 #dft 500 is 5s
vm.dirty_expire_centisecs=300    #dft 30000 is 30s
vm.drop_caches=3  #dft  0
vm.swappiness=100  #dft 60
vm.vfs_cache_pressure=133  #dft 100

总结

脚本我们现在的服务器已经在用了,报警的问题也解决了。

修改配置的方法还没有试过,试过后觉得不错请留言反馈!