前言
本文属于红帽性能调优课程之内存过度使用
如何防止内存被过度使用
许多应用程序相较于她们将实际使用的内存会请求分配更多的内存,另一些程序会请求内核分配超过系统可用内存的内存大小,解决这个问题的一个方法是调节 vm.overcommit_memory 参数,这个值可以是以下3个值
0:使用启发式算法,如果程序企图申请比可用内存更多的空间,则内核返回拒绝。但是当有大量的小内存申请时,内核依然可能overcommit。
1:不管系统是否有足够的内存,内核总是会分配内存申请
2:从不过载,内核只分配 swap 的大小加上物理内存百分比(默认一半)的内存,可以通过修改 vm.overcommit_ratio 修改默认值。
/proc/meminfo 中的 Committed_AS 预估了机器当前负载下可能需要的内存大小可以避免内存缺失(out-of-memory)
调节 swappiness
在内存中寻找不活跃的page( inactive page )会消耗大量 CPU 的时间,在大内存系统中,相较于将不活跃的页写入匿名也,而是找到并取消不活跃的页的映射,将之交换到磁盘会消耗更多的磁盘和 CPU 的资源。
在内存调优的课程中讲过,vm.swapness 值,这个值越大,系统更倾向于将数据 swap 出内存,来释放内存空间,反之越小,系统越倾向于丢弃 cached page ,来释放内存,在数据库场景下应设置 swappiness 以使。机器在大部分系统内存被消耗之前不会交换,vm.swappiness 默认值是60,在数据库场景中这个值建议在1-20之间,有些数据库开发人员会对这个值有一个建议,如果他们给出了参考值,那么应该优先遵循厂商的建议。
调节缓存页写入
另一个数据库服务器潜在的瓶颈是服务器磁盘读取数据的能力,如果数据库需要从磁盘读取数据,那么无论程序读取什么数据,该操作都会被阻塞,直到该操作的 I/O 请求返回了中断信号,除了将磁盘 IO 调度算法调节为 deadline ( moving the I/O to deadline ),让系统偏向于磁盘的读操作,管理员还可以修改配置管理系统如何将缓存的数据写入磁盘,缓存写入操作的时间越久意味着磁盘能更好的进行读取操作,但是进行这个配置。必须非常小心,如果机器掉电或者内核崩溃,这些缓存的数据依旧停留在 RAM 中,这些没有写入的数据将会丢失。
缓存文件写入的一些可调参数建议值:
vm.diry_background_ratio=3 #缓存的脏数据超过系统3%的内存,系统开始将脏数据写入到磁盘(不会强制写入)
vm.dirty_ratio=80 #脏数据超过系统内存80%,开始强制将脏数据写入磁盘 vm.dirty_expire_centisecs=500 #脏数据到期时间5秒
vm.dirty_writeback_centisecs=100 #每0.1秒系统检测一次脏数据是否到期,到期了就把脏数据刷到 I/O 子系统中
使用这个配置,缓存的数据会很快到期,但内核会在后台进行将脏数据写入磁盘的操作,除非发生很荒谬的事情,缓存的脏数据到了占到系统内存80%。
参考
/usr/share/doc/kernel-doc-*/Documentation/sysctl/vm.txt
/usr/share/doc/kernel-doc-*/Documentation/vm/overcommit-accounting
实验1、模拟程序申请大量内存,触发 Overcommitting Memory 查看 Committed_AS 值的变化
1、安装内存测试软件 bigmem
[root@server0 ~]# yum install bigmem -y
2、查看系统当前 Committed_AS 的值
[root@server0 ~]# grep Committed_AS /proc/meminfo
Committed_AS: 1136968 kB
3、使用 bigmem 申请20G虚拟内存
[root@server0 ~]# bigmem -v 20480
Attempting to allocate 20480 MebiBytes of virtual memory...
Allocated 10000MiB
Allocated 20000MiB
Press <Enter> to exit
4、重新开启一个终端查看内存过载
[root@server0 ~]# grep Commit /proc/meminfo
CommitLimit: 942588 kB
Committed_AS: 22196280 kB
5、返回上一个终端,按回车结束进程
实验2、禁止系统内存过载
1、安装内存测试软件 bigmem
[root@server0 ~]# yum install bigmem -y
2、允许程序最多申请2倍于物理内存+swap空间的内存
[root@server0 home]# sysctl -w vm.overcommit_ratio=200
vm.overcommit_ratio = 200
3、禁止操作系统实现内存过载
[root@server0 ~]# sysctl -w vm.overcommit_memory=2
vm.overcommit_memory = 2
4、查看 swap 与 RAM 空间的大小,计算出虚拟内存的阈值
[root@server0 home]# free
total used free shared buffers cached
Mem: 1885180 651660 1233520 17144 688 321584
-/+ buffers/cache: 329388 1555792
Swap: 0 0 0
5、查看当前系统允许申请的最大虚拟内存数量
[root@server0 home]# grep Commit /proc/meminfo
CommitLimit: 3770360 kB # swap 的空间为0 所以这个值为1885180*2
Committed_AS: 1150016 kB
6、用 bigmem 测试超过虚拟内存限制阈值的情况
[root@server0 ~]# bigmem -v 20450
Attempting to allocate 20450 MebiBytes of virtual memory...
Error allocating page, exiting
7、用 bigmem 测试临界与阈值的情况
[root@server0 ~]# bigmem -v 2457
Attempting to allocate 2457 MebiBytes of virtual memory...
Press <Enter> to exit
8、重新开启一个终端查看,查看 Committed_AS 与 CommitLimit 的值
[root@server0 home]# grep Commit /proc/meminfo
CommitLimit: 3770360 kB
Committed_AS: 3676032 kB
9、让系统尽可能将数据写入交换分区查看 Committed_AS 与 commitLimit 的变化
[root@server0 home]# sysctl -w vm.swappiness=100
vm.swappiness = 100
由于交换空间为0,所以不会有任何变化
10、创建一个1G交换分区观察 CommitLimit 的变化
[root@server0 home]# free
total used free shared buffers cached
Mem: 1885180 1818360 66820 17152 692 312392
-/+ buffers/cache: 1505276 379904
Swap: 1048572 0 1048572
[root@server0 home]# grep Commit /proc/meminfo
CommitLimit: 4818932 kB
Committed_AS: 1150320 kB
注意:如果发生过载系统将不能进行任何操作,因为不能再分配内存空间,reboot操作都执行不了,只能强制关机重启电脑,如果将此配置写入开机启动文件,同时将 commitLimit 值限制的非常小,将导致
系统不能正常启动,只能进入单用户模式修复。
[root@server0 ~]# ls
-bash: fork: Cannot allocate memory
[root@server0 ~]# reboot
-bash: fork: Cannot allocate memory
实现3:为数据库服务器创建一个调优文件
要求:
1、该文件的名称为 enterprise-database 并启用这个文件
2、这个文件要基于 throughput-performance 文件系统,确保标准大页可用
3、I/O 调度器设置为 deadline
4、脏页占据系统3%的内存后开始后台同步到磁盘
5、脏页占据系统80%的内存后,要强制写入磁盘
6、每100厘秒检测一次脏页是否到期
7、用默认的大页大小分配256MiB的大页空间
8、禁用透明大页分配
9、激活 tcp_fastopen 并设置为3
10、共享内存段的总大小为2TiB
11、单个信号的操作数量设置为100,其余的信号用默认值
12、禁止系统内存过度使用不能超过2倍的 RAM 加上 swap 的大小
13、机器可以使用swap,但应该尽可能的少用
参考答案
[root@server0 home]# mkdir /etc/tuned/enterprise-database
[root@server0 ~]# cat /etc/tuned/enterprise-database/tuned.conf
#
# tuned configuration
#
[main]
include=throughput-performance
[vm]
transparent_hugepages=never
[sysctl]
vm.dirty_background_ratio=3
vm.dirty_ratio=80
vm.dirty_writeback_centisecs=100
vm.nr_hugepages=128
vm.overcommit_ratio=200
vm.swappiness=1
kernel.shmall="536870912"
kernel.sem="250 32000 100 128"
net.ipv4.tcp_fastopen=3
[primary]
type=disk
elevator=deadline
[root@server0 ~]# tuned-adm profile enterprise-database