在最近一两个月的运维过程中,多次遇到了系统OOM Killer的问题,其中有Mongodb的OOM,也遇到了Mysql的OOM。在这里我整理了一下遇到OOM时候基本的处理思路,处理的主线思路是依据Percona的文章What To Do When MySQL Runs Out of Memory。
出现Out of Memory
在应用出现Out of memory的时候,应用程序的日志一般都有一个特点,那就是没有任何的报错信息,就这么突然之间死掉了。
所以在发现应用出现毫无报错直接就到了启动日志的时候就要考虑是不是出现了Out of Memory,操作系统调用了oom killer直接杀死了进程导致的。
如何开始排查
在上面提到的文章中结束了排查的思路,具体来说就是两个部分,首先需要在操作系统层面和配置层面做排查;另外就是需要详细的排查Mysql服务器的内部。
Part1 Linux OS and Config Check
a) 检查Mysql错误日志以及OS 日志
在Mysql的错误中主要观察在发生宕机前的一段时间是否有其它的告警信息,是否能观察到宕机的原因。对于因为Out of memory被kill的进程来说,很可能看不异常的日志信息,突然就发现Mysqld启动。
另外就是要观察发生OOM的时候主机产生的dump信息,这些信息可以在/var/log/memssage中查看到,在这个文件中可以查看在发生OOM的时候服务器上面的内存负载情况。
b) 查看主机的内存信息
free -m
cat /proc/meminfo
c) 使用top命令查看那些应用占用了大量的内存
如果已经发生了oom,使用top不一定能抓到当时的情况,所以建议在系统上做好监控,比如osw或者nmon监控都可以。
d) 查看mysql的配置文件
主要查看mysql中各个pool的配置,理论最大分配为:
mysql used mem = key_buffer_size + query_cache_size + tmp_table_size
+ innodb_buffer_pool_size + innodb_additional_mem_pool_size
+ innodb_log_buffer_size
+ max_connections * (
read_buffer_size + read_rnd_buffer_size
+ sort_buffer_size+ join_buffer_size
+ binlog_cache_size + thread_stack
)
Part 2: Checks inside MySQL
a) 表缓存
b) show engine performance_schema status 并查看最后一行。
c) show engine innodb status\G
在这里主要关心buffer_pool的内存分配情况
d) 确认memory类型的表对内存的占用
select * from information_schema.tables where engine='MEMORY';
e) 对于5.7以上的版本中可以使用performance_schema的功能去查看。具体的内存分配情况
UPDATE setup_instruments SET ENABLED = 'YES'
WHERE NAME LIKE 'memory/%';
select event_name, current_alloc, high_alloc from sys.memory_global_by_current_bytes where current_count > 0;
--按事件分类
select event_name,sys.format_bytes(current_number_of_bytes_used)
from performance_schema.memory_summary_global_by_event_name
order by current_number_of_bytes_used desc limit 10;
--按账号分类
select user,host,sum(CURRENT_NUMBER_OF_BYTES_USED)/1024/1024 as mb_current_used
from performance_schema.memory_summary_by_account_by_event_name
where host<>'localhost'
group by user,host;
--线程占用
select m.thread_id tid,m.user,esc.DIGEST_TEXT,m.current_allocated,m.total_allocated
from sys.memory_by_thread_by_current_bytes m,
performance_schema.events_statements_current esc
where m.thread_id=esc.thread_id;
知识扩展
Out of memory与内存的overcommit
要理解OOM killer的功能,那么首先就需要先理解linux的Overcommit内存分配机制。在linux系统中,应用程序对内存的使用流程是先申请足够多的内存,然后在有需要的时候在使用它,那么就意味着申请之后不一定立刻使用了这些内存。那么这就遇到了一个是否允许内存超配的问题,linux将这个问题抛给了用户,它提供一些参数让用户自己去控制是否允许超配的发生。如下就从如何监控内存超配以及相关参数两个方面去理解Out of memory。
如何查看内存分配
要查看当前系统的内存分配情况很简单,查看/proc/meminfo文件里面的内容计即可。
cat /proc/meminfo
MemTotal: 32780168 kB
MemFree: 2385400 kB
MemAvailable: 7160960 kB
Buffers: 0 kB
Cached: 4887100 kB
SwapCached: 161032 kB
Active: 24986472 kB
Inactive: 4739544 kB
Active(anon): 22645944 kB
Inactive(anon): 2227172 kB
Active(file): 2340528 kB
Inactive(file): 2512372 kB
Unevictable: 16 kB
Mlocked: 16 kB
SwapTotal: 4194300 kB
SwapFree: 372 kB
Dirty: 228 kB
Writeback: 0 kB
AnonPages: 24677632 kB
Mapped: 58724 kB
Shmem: 34164 kB
Slab: 364668 kB
SReclaimable: 325484 kB
SUnreclaim: 39184 kB
KernelStack: 10960 kB
PageTables: 66508 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 20584384 kB <----重点
Committed_AS: 33172904 kB <----重点
VmallocTotal: 34359738367 kB
VmallocUsed: 198756 kB
VmallocChunk: 34359341052 kB
HardwareCorrupted: 0 kB
AnonHugePages: 53248 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 124800 kB
DirectMap2M: 4069376 kB
DirectMap1G: 31457280 kB
重点看两个输出CommitLimit和Committed_AS。
CommitLimit,表示当前系统中内存的分配限制,内存分配超过了这个大小就意味着存在超配现象(注意,存在超配并不代表异常)
该数值的计算公式为“(物理内存*vm.overcommit_ratio/100)+SWAP”,overcommit_ratio的值默认为50,所以对于一个32G物理内存,4G交换空间的系统来说,CommitLimit的大小大概为20G左右。
Committed_AS,表示系统中的应用一共申请了多少的内存。Committed_AS-CommitLimit的大小就是超配的内存量。
相关参数
vm.overcommit_ratio 默认值为50,该参数会影响CommitLimit的大小
overcommit取值范围:
0 是默认值,在遇到内存空间申请的时候linux会根据当前系统的状态决定允许多少超配的问题,值为0的时候是允许超配的,但不总是允许超量申请。
1 表示linux总是允许超配的情况发生。应用不管申请多少内存linux都返回“同意”,直到把内存全部使用完。
2 不允许超配的发生,允许分配的内存=”物理内存*vm.overcommit_ratio/100)+SWAP”
OOM Dump信息
关于OOM的触发机制
在系统性能不足的过程中,首先会触发一系列的内存回收工作,比如你可能会观察到kswapd进程会占用较多的CPU资源,这是因为kswapd进程是核心换页进程,在内存不足的情况下会触发linux的换页操作。
OOM DUMP的输出
systemd: Removed slice User Slice of root.
/*在下面的输出可以看到是Mysqld进程触发了oom-killer */
kernel: mysqld invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0
kernel: mysqld cpuset=/ mems_allowed=0
kernel: CPU: 6 PID: 61106 Comm: mysqld Kdump: loaded Not tainted 3.10.0-1160.49.1.el7.x86_6
kernel: Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platfor
kernel: Call Trace:
kernel: [<ffffffffb0b83539>] dump_stack+0x19/0x1b
kernel: [<ffffffffb0b7e5d8>] dump_header+0x90/0x229
kernel: [<ffffffffb0506992>] ? ktime_get_ts64+0x52/0xf0
kernel: [<ffffffffb055e01f>] ? delayacct_end+0x8f/0xb0
kernel: [<ffffffffb05c254d>] oom_kill_process+0x2cd/0x490
kernel: [<ffffffffb05c1f3d>] ? oom_unkillable_task+0xcd/0x120
kernel: [<ffffffffb05c2c3a>] out_of_memory+0x31a/0x500
kernel: [<ffffffffb05c9854>] __alloc_pages_nodemask+0xad4/0xbe0
kernel: [<ffffffffb06193b8>] alloc_pages_current+0x98/0x110
kernel: [<ffffffffb05be007>] __page_cache_alloc+0x97/0xb0
kernel: [<ffffffffb05c0fa0>] filemap_fault+0x270/0x420
kernel: [<ffffffffc064691e>] __xfs_filemap_fault+0x7e/0x1d0 [xfs]
kernel: [<ffffffffc0646b1c>] xfs_filemap_fault+0x2c/0x30 [xfs]
kernel: [<ffffffffb05ee7aa>] __do_fault.isra.61+0x8a/0x100
kernel: [<ffffffffb05eed5c>] do_read_fault.isra.63+0x4c/0x1b0
kernel: [<ffffffffb05f65a0>] handle_mm_fault+0xa20/0xfb0
kernel: [<ffffffffb0b90653>] __do_page_fault+0x213/0x500
kernel: [<ffffffffb0b90975>] do_page_fault+0x35/0x90
kernel: [<ffffffffb0b8c778>] page_fault+0x28/0x30
/* 这里列出了Mem_info的信息,单位是“个页面”,如果要换算成为大小,需要乘以4K */
kernel: Mem-Info:
kernel: active_anon:15307537 inactive_anon:741942 isolated_anon:0#012 active_file:0 inactiv
e_file:2563 isolated_file:15#012 unevictable:0 dirty:0 writeback:0 unstable:0#012 slab_reclaimable:52993 slab_unreclaimable:28
120#012 mapped:9030 shmem:37735 pagetables:37829 bounce:0#012 free:82480 free_pcp:60 free_cma:0
kernel: Node 0 DMA free:15872kB min:16kB low:20kB high:24kB active_anon:0kB inactive_anon:0
kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15988kB managed:15904kB mlo
cked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:32kB kernel_stack:0kB pagetables
:0kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
kernel: lowmem_reserve[]: 0 2827 64229 64229
kernel: Node 0 DMA32 free:249320kB min:2972kB low:3712kB high:4456kB active_anon:1997908kB
inactive_anon:499632kB active_file:724kB inactive_file:2248kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:31
29152kB managed:2895648kB mlocked:0kB dirty:0kB writeback:0kB mapped:2532kB shmem:7008kB slab_reclaimable:58512kB slab_unrecla
imable:32256kB kernel_stack:2592kB pagetables:4456kB unstable:0kB bounce:0kB free_pcp:120kB local_pcp:0kB free_cma:0kB writeba
ck_tmp:0kB pages_scanned:236 all_unreclaimable? no
kernel: lowmem_reserve[]: 0 0 61401 61401
kernel: Node 0 Normal free:64624kB min:64592kB low:80740kB high:96888kB active_anon:5923224
0kB inactive_anon:2468136kB active_file:0kB inactive_file:8004kB unevictable:0kB isolated(anon):0kB isolated(file):60kB presen
t:63963136kB managed:62878140kB mlocked:0kB dirty:0kB writeback:0kB mapped:33588kB shmem:143932kB slab_reclaimable:153460kB sl
ab_unreclaimable:80192kB kernel_stack:9696kB pagetables:146860kB unstable:0kB bounce:0kB free_pcp:116kB local_pcp:0kB free_cma
:0kB writeback_tmp:0kB pages_scanned:128 all_unreclaimable? no
kernel: lowmem_reserve[]: 0 0 0 0
kernel: Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 2*64kB (U) 1*128kB (U) 1*256kB (U) 0*512kB 1*
1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15872kB
xxxxxxxxxx xxxxxxxxxx kernel: Node 0 DMA32: 829*4kB (UEM) 390*8kB (UEM) 341*16kB (UEM) 818*32kB (UEM) 406*64kB (U
EM) 152*128kB (UEM) 138*256kB (UEM) 131*512kB (UEM) 62*1024kB (UM) 0*2048kB 0*4096kB = 249396kB
xxxxxxxxxx xxxxxxxxxx kernel: Node 0 Normal: 17458*4kB (UEM) 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0
*1024kB 0*2048kB 0*4096kB = 69832kB
kernel: Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
kernel: Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
kernel: 63649 total pagecache pages
kernel: 22766 pages in swap cache
kernel: Swap cache stats: add 2002898, delete 1980201, find 394290645/394536357
kernel: Free swap = 0kB
kernel: Total swap = 4194300kB
kernel: 16777069 pages RAM
kernel: 0 pages HighMem/MovableOnly
kernel: 329646 pages reserved
/* 这里显示了发生OOM时候,系统的内存使用情况,单位是“个页面” */
kernel: [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name
kernel: [ 765] 0 765 26141 13500 58 46 0 systemd-journa
kernel: [ 789] 0 789 68076 30 31 89 0 lvmetad
kernel: [ 796] 0 796 11552 2 25 333 -1000 systemd-udevd
kernel: [ 1070] 0 1070 13883 32 27 80 -1000 auditd
kernel: [ 1097] 0 1097 42037 1 38 352 0 VGAuthService
kernel: [ 1098] 0 1098 68768 185 55 171 0 vmtoolsd
kernel: [ 1102] 81 1102 14553 85 32 70 -900 dbus-daemon
kernel: [ 1106] 998 1106 5634 28 17 47 0 chronyd
kernel: [ 1115] 0 1115 5420 69 15 27 0 irqbalance
kernel: [ 1116] 0 1116 6596 49 18 34 0 systemd-logind
kernel: [ 1117] 999 1117 153084 135 63 1799 0 polkitd
kernel: [ 1123] 0 1123 31605 110 18 68 0 crond
kernel: [ 1128] 0 1128 27552 1 9 31 0 agetty
kernel: [ 1454] 0 1454 143571 146 98 3205 0 tuned
kernel: [ 1457] 0 1457 111747 6909 108 111 0 rsyslogd
kernel: [ 1461] 0 1461 28246 24 57 235 -1000 sshd
kernel: [ 1499] 997 1499 20284 13 39 186 0 zabbix_agentd
kernel: [ 1504] 997 1504 20284 614 40 174 0 zabbix_agentd
kernel: [ 1505] 997 1505 20314 56 40 198 0 zabbix_agentd
kernel: [ 1506] 997 1506 20314 57 40 198 0 zabbix_agentd
kernel: [ 1508] 997 1508 20314 57 40 198 0 zabbix_agentd
kernel: [ 1510] 997 1510 20284 32 39 202 0 zabbix_agentd
kernel: [ 1734] 0 1734 22662 19 42 264 0 master
kernel: [ 1740] 89 1740 22705 15 45 263 0 qmgr
kernel: [123593] 27 123593 20502462 15978204 36236 1016376 0 mysqld
kernel: [61611] 0 61611 15246 254 23 0 0 pbx_exchange
kernel: [63126] 0 63126 490324 884 114 0 0 vnetd
kernel: [63127] 0 63127 492782 1687 121 0 0 vnetd
kernel: [63184] 0 63184 26936 339 43 0 0 vnetd
kernel: [63190] 0 63190 35282 513 60 0 0 bpcd
kernel: [63315] 0 63315 251752 1818 168 0 0 nbdisco
kernel: [39983] 89 39983 22688 276 45 0 0 pickup
kernel: Out of memory: Kill process 123593 (mysqld) score 973 or sacrifice child
kernel: Killed process 123593 (mysqld), UID 27, total-vm:82009848kB, anon-rss:63913504kB, f
ile-rss:0kB, shmem-rss:0kB
systemd: mysqld.service: main process exited, code=killed, status=9/KILL
systemd: Unit mysqld.service entered failed state.
systemd: mysqld.service failed.
systemd: mysqld.service holdoff time over, scheduling restart.
systemd: Stopped MySQL Server.
systemd: Starting MySQL Server...
systemd: Started MySQL Server.
total_vm 使用的虚拟内存的大小。
RSS(重点):total_vm中的一部分是分配在内存上面的,这一部分分配的内存上面的大小就可以理解为进程实际使用内存的大小。而RSS=(anon_rss+file_rss),anon_rss就是通过malloc()分配的内存,比如mysql里面各个pool的内存就是通过malloc()分配的,file_rss表示为文件分配的内存,比如进程需要使用到的一些共享库文件。另外需要注意的是
1、RSS包含了共享库文件占用的空间,但是共享库文件可能被很多的进程同时使用到,那就意味着所有进程的RSS加起来可能会超过总内存的大小。
2、RSS不包含进程在SWAP里面占用的空间。
关于free 命令输出的理解
[root@vm002 ~]# free -m
total used free shared buff/cache available
Mem: 1726 1164 66 16 496 349
Swap: 1951 0 1951
[root@vm002 ~]#
这个输出大家一定是非常熟悉的,但是我这里稍微整理了一下每个数字的具体含义。
free,表示空闲的物理内存
used,已经被使用的内存。在被使用的内存中主要有三部分去向,一个是被进程实际占用了,这部分参考RSS;另外一个就是用于SLAB,linux对于一些经常被访问的对象会放到一个池中用来提高性能,这个池就是SLAB。最后的一个大头就是页表PageTable。
buff/cache 这部分在一定程度上面是可以回收的,其中buffer对于内存页的缓存,cache对应文件的缓存。buff/cache占用的内存可以通过drop_cache参数进行回收
echo 1 > /proc/sys/vm/drop_caches <---清理pagecache,对应/proc/meminfo里面的cached
echo 2 > /proc/sys/vm/drop_caches <---清理释放dentries和inodes
echo 3 > /proc/sys/vm/drop_caches <---清理所有的缓存
但是,我们完全可以通过配置vm参数的方式去让主机自动清理这些缓存,没有必要使用drop_caches这样暴力的方式去清理缓存。