管理内存是操作系统的核心职责之一. Linux 系统把内存分页管理, 每一页 (page) 的大小可以用命令 getconf PAGESIZE 查看, 单位是字节 (Byte),Linux 默认的页大小为 4KB.
- [root@localhost ~]# getconf PAGESIZE
- 4096
内存的总体情况可以通过 cat /proc/meminfo 查看
- [root@localhost ~]# cat /proc/meminfo
- MemTotal: 65751096 kB
- MemFree: 64077144 kB
- MemAvailable: 64061216 kB
- Buffers: 2076 kB
- Cached: 404000 kB
- SwapCached: 0 kB
- Active: 759688 kB
- Inactive: 177952 kB
- Active(anon): 532748 kB
- Inactive(anon): 17212 kB
- Active(file): 226940 kB
- Inactive(file): 160740 kB
- Unevictable: 0 kB
- Mlocked: 0 kB
- SwapTotal: 33554428 kB
- SwapFree: 33554428 kB
- Dirty: 8 kB
- Writeback: 0 kB
- AnonPages: 531796 kB
- Mapped: 54212 kB
- Shmem: 18396 kB
- Slab: 194488 kB
- SReclaimable: 72256 kB
- SUnreclaim: 122232 kB
- KernelStack: 13360 kB
- PageTables: 12020 kB
- NFS_Unstable: 0 kB
- Bounce: 0 kB
- WritebackTmp: 0 kB
- CommitLimit: 66429976 kB
- Committed_AS: 2371680 kB
- VmallocTotal: 34359738367 kB
- VmallocUsed: 415224 kB
- VmallocChunk: 34308620284 kB
- HardwareCorrupted: 0 kB
- AnonHugePages: 290816 kB
- HugePages_Total: 0
- HugePages_Free: 0
- HugePages_Rsvd: 0
- HugePages_Surp: 0
- Hugepagesize: 2048 kB
- DirectMap4k: 217920 kB
- DirectMap2M: 8124416 kB
- DirectMap1G: 60817408 kB
本文主要总结一下在运维实践中遇到的跟内存有关的问题或技巧.
buffer cache 和 swap
free 是系统管理员查看服务器内存情况常用的命令, 在 CentOS7.4 上输出内容如下:
- [root@localhost ~]# free
- total used free shared buff/cache available
- Mem: 65751096 1079756 64070284 18396 601056 64054484
- Swap: 33554428 0 33554428
从 CentOS7 系列版本开始, buffer 和 cache 被合并为一列显示了, 在之前的版本中是分开的. 虽然被合并显示, 但是它们保存的内容是不同的.
l Buffer-- 顾名思义是缓冲区的意思, 由于内存访问速度远超硬盘等块设备, 所以 Linux 系统把文件系统中访问过的元数据缓存在 buffer 中, 以提高下次访问的速度.
l Cache--cache 是缓存区, 在内存足够的情况下, Linux 系统总是倾向于把打开过的程序, 文件内容缓存在内存中, 所以长时间运行的服务器 cache 都比较大, 这么做是为了下次访问这些文件更快. 当有程序申请内存且空闲内存不足时, 系统会释放 cache 以腾出空间.
l Swap-- 在以前内存还是很金贵的资源时, 为了 "扩展" 服务器有限的内存, Linux 允许在块设备上划分出一块空间, 将内存中不经常被访问的页置换到这里, 腾出内存给处于活跃状态的进程, 这个空间就是交换分区 swap. 由于硬盘比内存访问性能低, 所以使用 swap 分区会降低性能. 现在的服务器有一两百 GB 内存已经不稀奇了, 似乎 swap 已经没有存在的意义了, 其实不然, 即使 swap 现在已经很少被用到了, 我们在部署服务器时仍应该分配一个合理的空间给 swap, 有 swap 分区作为缓冲, 可以避免系统因内存不足开始自动 kill 进程引起宕机, 给运维人员处理内存饱和的问题争取时间, 性能损失总比宕机要好.
实际情况中, 有时会遇到有的服务器内存被 cache 占用完, 系统开始把部分内存置换到 swap 中, 导致性能急剧下降. 这种情况下可以清理 cache, 释放出内存给进程用. 清理 cache 的过程如下:
- [root@localhost ~]# sync
- [root@localhost ~]# echo 3> /proc/sys/vm/drop_caches
为了安全, 清理 cache 前要先执行 sync, 使内存中的 dirty 数据落盘.
drop_caches 取值范围为 1,2,3,
1 表示清理页面缓存;
2 表示清理页面缓存和目录缓存;
3 表示清理页面缓存, 目录缓存和 inode 缓存.
为了减小系统使用 swap 分区的倾向, 还有个内核参数可以调整 --swappiness.swappiness 的取值范围是 0 到 100, 数值越大系统使用 swap 分区的倾向就越大, 为 0 时表示完全不使用 swap. 鉴于上面介绍过 swap 分区存在的必要性, 我们不应该把 swappiness 设置为 0, 而应该设置为 1, 让系统尽可能避免使用 swap 分区, 同时并不完全放弃使用 swap 的可能.
- [root@localhost ~]# echo "vm.swappiness = 1">> /proc/sys/vm/swappiness
- [root@localhost ~]# sysctl -p
- vm.swappiness = 1
- KSM
KSM(Kernel SamePage Merging) 是一种压缩内存的技术, 顾名思义就是将内存中内容相同的页进行合并以节省内存空间, 当有进程需要修改该内存页的内容时, 再将其拷贝一份再修改, 也就是 "写时复制 copy on write".KSM 最早是为了优化 qemu-kvm 宿主机内存使用而诞生的, 当一台宿主机上运行多个相同操作系统的虚拟机时, 有很多内存页内容相同可以合并. KSM 在 CentOS6 和 CentOS7 上是默认打开的, 有两个相关服务:
l ksm
l Ksmtuned
ksmtuned 依赖 ksm 服务, 它的主体 / usr/sbin/ksmtuned 是一个 shell 脚本, 通过读取配置文件 / etc/ksmtuned.conf 中的参数, 对 ksm 的行为进行控制. 在 ksmtuned 的控制下, ksm 并不是一直在工作, 它只在参数限定的条件达到的情况下工作, ksm 是否在工作可以通过 cat /sys/kernel/mm/ksm/run 命令查看, 0 表示 ksm 没有在工作, 1 表示 ksm 在工作.
通过分析 / usr/sbin/ksmtuned, 可以了解配置文件中几个主要参数的含义:
l KSM_MONITOR_INTERVAL=60 默认值为 60, 单位秒, 表示每隔 60 秒, ksmtuned 工作一次, 扫描系统内存使用情况并据此调整 ksm 参数. 数值越小 ksmtuned 工作频率越高, 对 cpu 消耗越多;
l KSM_SLEEP_MSEC=10 默认值为 10, 最小值也为 10, 单位毫秒, ksm 扫描内存页的间隔, 数值越小 ksm 扫描内存页的频率越高, 对 cpu 消耗越多. 对应的内核参数是 / sys/kernel/mm/ksm/sleep_millisecs;
- l KSM_NPAGES_BOOST=300
- KSM_NPAGES_DECAY=-50
- KSM_NPAGES_MIN=64
- KSM_NPAGES_MAX=1250
这一组参数用于设置 ksm 一次扫描多少内存页, 对应内核参数 / sys/kernel/mm/ksm/pages_to_scan.pages_to_scan 的值在 KSM_NPAGES_MIN 和 KSM_NPAGES_MAX 之间波动, 如果空闲内存大于临界值, 需要降低 ksm 工作强度, 则每次 ksmtuned 工作都会调低 pages_to_scan 300(KSM_NPAGES_BOOST) 页; 反之, 需要增强 ksm 工作强度, 则每次 ksmtuned 工作会调高 pages_to_scan 50(KSM_NPAGES_DECAY) 页.
- l KSM_THRES_COEF=20
- KSM_THRES_CONST=2048
这两个参数用于设置一个内存临界值, 总内存的 20%(KSM_THRES_COEF) 和 2048MB(KSM_THRES_CONST) 两者之间取大者. 当 "所有 qemu 进程的内存" 和临界值之和小于 "总内存", 并且空闲内存大于临界值时, ksm 停止工作,/sys/kernel/mm/ksm/run 被置 0; 其他情况下, ksm 处于工作状态,/sys/kernel/mm/ksm/run 被置 1. 如果想让 ksm 早点开始工作, 可以调高临界值.
常见的 ksmtuned.conf 配置如下:
- # Configuration file for ksmtuned.
- # How long ksmtuned should sleep between tuning adjustments
- KSM_MONITOR_INTERVAL=60
- # Millisecond sleep between ksm scans for 16Gb server.
- # Smaller servers sleep more, bigger sleep less.
- KSM_SLEEP_MSEC=10
- KSM_NPAGES_BOOST=300
- KSM_NPAGES_DECAY=-50
- KSM_NPAGES_MIN=64
- KSM_NPAGES_MAX=1250
- KSM_THRES_COEF=20
- KSM_THRES_CONST=2048
- # uncomment the following if you want ksmtuned debug info
- LOGFILE=/var/log/ksmtuned
- DEBUG=1
日志 / var/log/ksmtuned 记录了 ksmtuned 每次执行的结果:
- [root@localhost ~]# tail -100f /var/log/ksmtuned
- Thu Jan 18 11:29:18 CST 2018: total 65751096
- Thu Jan 18 11:29:18 CST 2018: sleep 10
- Thu Jan 18 11:29:18 CST 2018: thres 13150219
- Thu Jan 18 11:30:18 CST 2018: committed 0 free 64470428
- Thu Jan 18 11:30:18 CST 2018: 13150219 <65751096 and free> 13150219, stop ksm
我的测试机内存比较空闲, ksm 一直处于 stop 状态, 下面我把 KSM_THRES_COEF 调大到 100 然后 systemctl restart ksmtuned, 强行让 ksm 工作, 然后看看效果:
- [root@localhost ~]# cat /sys/kernel/mm/ksm/run
- 1
- [root@localhost ~]# tail -10f /var/log/ksmtuned
- Thu Jan 18 15:05:05 CST 2018: sleep 10
- Thu Jan 18 15:05:05 CST 2018: thres 65751096
- Thu Jan 18 15:06:05 CST 2018: committed 0 free 64467712
- Thu Jan 18 15:06:05 CST 2018: 65751096> 65751096, start ksm
- Thu Jan 18 15:06:05 CST 2018: 64467712 <65751096, boost
- Thu Jan 18 15:06:05 CST 2018: KSMCTL start 300 10
- Thu Jan 18 15:07:05 CST 2018: committed 0 free 64468624
- Thu Jan 18 15:07:05 CST 2018: 65751096> 65751096, start ksm
- Thu Jan 18 15:07:05 CST 2018: 64468624 <65751096, boost
- Thu Jan 18 15:07:05 CST 2018: KSMCTL start 600 10
/sys/kernel/mm/ksm/pages_sharing 记录了有多少内存页正在使用被合并的内存页, 也就是实际节省的内存页数. 页数乘以 PAGESIZE 就可以计算出实际节省的内存, 命令如下:
- echo "$(($(cat /sys/kernel/mm/ksm/pages_sharing)*$(getconf PAGESIZE)/1024/1024))MB"
- NUMA
给有两路 cpu 的服务器插过内存的同学应该知道, 每路 cpu 都有自己的内存条插槽. 这是解决多 cpu 扩展问题的一种架构 --NUMA,Non-Uniform Memory Access, 非一致内存访问. 为什么叫 "非一致" 呢? 因为一个 cpu 访问自己的本地内存比访问别的 cpu 的内存速度要快, 所以叫非一致.
numactl 是一个管理 numa 的命令行工具, 如果系统默认没有这个命令, 可以通过 yum install numactl 安装. numactl -hardware 可以查看 cpu 和内存的硬件情况, 下面是一台 4 路服务器上执行命令的结果:
- [root@localhost ~]# numactl --hardware
- available: 4 nodes (0-3)
- node 0 cpus: 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60
- node 0 size: 16338 MB
- node 0 free: 15279 MB
- node 1 cpus: 1 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61
- node 1 size: 16384 MB
- node 1 free: 15762 MB
- node 2 cpus: 2 6 10 14 18 22 26 30 34 38 42 46 50 54 58 62
- node 2 size: 16384 MB
- node 2 free: 15646 MB
- node 3 cpus: 3 7 11 15 19 23 27 31 35 39 43 47 51 55 59 63
- node 3 size: 16384 MB
- node 3 free: 15861 MB
- node distances:
- node 0 1 2 3
- 10 20 30 20
- 20 10 20 30
- 30 20 10 20
- 20 30 20 10
4 路 cpu 就是 4 个 numa nodes, 每个 nodes 有 16G 的本地内存, 整个服务器总共有 64G 物理内存.
numastat -c 可以查看指定进程的内存使用情况:
[root@localhost ~]# numastat -c nginx
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Node 2 Node 3 Total
- ------------- ------ ------ ------ ------ -----
- (nginx) 0 0 1 0 1
- (nginx) 0 0 2 0 3
- (nginx) 1 0 1 0 3
- (nginx) 1 1 1 0 3
- (nginx) 0 0 2 0 3
- (nginx) 1 0 1 0 3
- (nginx) 0 1 1 0 3
- (nginx) 0 0 2 0 3
- (nginx) 1 0 1 0 3
- (nginx) 0 0 2 0 3
- (nginx) 0 1 1 0 3
- (nginx) 1 0 1 0 3
- (nginx) 0 0 2 0 3
- (nginx) 0 0 2 0 3
- (nginx) 0 1 1 0 3
- (nginx) 1 0 1 0 3
- (nginx) 0 0 2 0 3
- (nginx) 0 0 2 0 3
- (nginx) 0 1 1 0 3
- (nginx) 2 0 1 1 3
- (nginx) 0 0 2 0 3
- (nginx) 0 0 2 0 3
- (nginx) 1 1 1 0 3
- (nginx) 1 0 1 0 3
- (nginx) 0 0 2 0 3
- ------------- ------ ------ ------ ------ -----
Total 18 6 35 5 65
上面的输出可以看出各个 nginx 进程的内存分散在了 4 个 node 上.
CentOS6 和 Centos7 上都有一个叫 numad 的服务, 这个服务负责在 numa 架构下调度各节点的内存分配, 使内存分配具有 cpu 亲和性, 例如一个线程运行在 cpu0 上, 它申请的内存页将尽量从 cpu0 的本地内存中分配. 这个服务默认没有开启, 因为这种内存分配策略并不适合所有场景, 当服务器上运行了很多应用程序或很多虚拟机时, numad 具有 cpu 亲和性的内存分配方式可以提高性能, 但是如果整个系统只运行一个高内存消耗的程序如数据库, numad 反而会导致内存分配不均而降低性能, 因此数据库服务器建议不要开启 numad 服务.
Linux 系统中还有一个 numa 自动平衡策略, 系统通过自动调配内存以求在各个 numa nodes 的内存使用率处于相对平衡的状态, 这个策略可以通过修改内核参数 / proc/sys/kernel/numa_balancing 控制. 关闭 numa 自动平衡策略: echo 0> /proc/sys/kernel/numa_balancing; 开启 numa 自动平衡策略: echo 1> /proc/sys/kernel/numa_balancing. 这个策略系统默认也没开启.
来源: http://www.bubuko.com/infodetail-2557268.html