记一次MySQL OOM分析处理过程,聊聊我的思路。技术人人都可以磨炼,但处理问题的思路和角度各有不同,希望这篇文章可以抛砖引玉。
以一个例子为切入点
一、问题背景
某业务模块反馈数据库最近出现过几次连接失败的情况,频繁的时候一天发生三四次,虽然过一会就恢复了,还是想排查一下具体原因,消除潜在隐患。
今天我们就从这个问题开始来聊一聊MySQL 的OOM。
基础环境:
- 主机类型:x3850 X6
- 操作系统:DB:CentOS Linux release 7.4.1708、APP:CentOS Linux release 7.2.1511 (Core)
- 存储:IBM存储,2TB,MULTIPATH
- 内存:64 G
- CPU型号:E7-4830 v3 @ 2.10GHz ( 4 U * 12 core)
- CPU核数:32CORE
- 数据库环境:5.7.27
问题现象:MySQL OOM
简单说明:
在很多应用场景中,数据库的稳定性直接决定了系统的稳定性。本文介绍一些通用的数据库基础配置小技巧,健壮的数据库不能解决所有的宕机问题,但是却能增加数据库运行的稳定性。
二、分析说明
- 通过分析日志定位、分析故障原因;
- 追溯历史数据,分析关键指标的历史波动,这些关键指标可以用来做为数据库健康度参考指标。
- 用实际数据来验证推断,排除掉其它干扰因素,定位数据库问题的根本原因,帮助快速修复。
三、疑问点排查及分析思路
1、分析最近一次异常期间数据库日志
根据业务模块反馈,最近一次异常数据库直接挂了,查看日志确实有过重启动作。
2021-08-26T13:43:41.895472Z 0 [Note] --secure-file-priv is set to NULL. Operations related to importing and exporting data are disabled
2021-08-26T13:43:41.896440Z 0 [Note] /usr/local/mysql/bin/mysqld (mysqld 5.7.27-log) starting as process 1846 ...
2021-08-26T13:43:42.312852Z 0 [Note] InnoDB: PUNCH HOLE support available
2021-08-26T13:43:42.313160Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2021-08-26T13:43:42.313170Z 0 [Note] InnoDB: Uses event mutexes
2021-08-26T13:43:42.313173Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
2021-08-26T13:43:42.313203Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
2021-08-26T13:43:42.313209Z 0 [Note] InnoDB: Using Linux native AIO
2021-08-26T13:43:42.329628Z 0 [Note] InnoDB: Number of pools: 1
2021-08-26T13:43:42.357426Z 0 [Note] InnoDB: Using CPU crc32 instructions
2021-08-26T13:43:42.377711Z 0 [Note] InnoDB: Initializing buffer pool, total size = 20480M, instances = 1
2021-08-26T13:43:42.562303Z 0 [Note] InnoDB: Completed initialization of buffer pool
2021-08-26T13:43:42.614060Z 0 [Note] InnoDB: If the mysqld execution user is authorized, page cleaner thread priority can be changed. See the man page of setpriority().
2021-08-26T13:43:42.667929Z 0 [Note] InnoDB: Highest supported file format is Barracuda.
2021-08-26T13:43:42.980652Z 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2021-08-26T13:43:43.079802Z 0 [Note] InnoDB: 96 redo rollback segment(s) found. 96 redo rollback segment(s) are active.
2021-08-26T13:43:43.079839Z 0 [Note] InnoDB: 32 non-redo rollback segment(s) are active.
2021-08-26T13:43:43.088029Z 0 [Note] InnoDB: 5.7.27 started; log sequence number 323029387
2021-08-26T13:43:43.422345Z 0 [Note] InnoDB: Buffer pool(s) load completed at 210826 21:43:43
2021-08-26T13:43:43.527409Z 0 [Note] Server hostname (bind-address): '*'; port: 3306
2021-08-26T13:43:43.527453Z 0 [Note] IPv6 is available.
2021-08-26T13:43:43.527486Z 0 [Note] - '::' resolves to '::';
2、查看当前内存 cpu 的使用
使用 cat /proc/meminfo 查看系统内存使用情况, 64G 内存,free所剩不多大约2G(此时数据库还是正常运行的),可疑的是 buffer/cache 也不多,使用 top 查看 MySQL RES 占用 20 多 G 左右。
[root@mysql ~]# cat /proc/meminfo
MemTotal: 65108800 kB
MemFree: 2097152 kB
Buffers: 21160 kB
Cached: 4996040 kB
3、分析最近一次异常期间操作系统日志
因为内存使用率实在太高,怀疑mysqld进程被kill,所以查看下系统日志,发现了重启原因。
master kernel:Out of memory:Kill process 1482 (mysqld) score 351 or sacrifice child
master kernel:Killd process 1482 total-vm:65108800kB,anon-rss:23078724kB,file-rss:0kB
master systemd:Starting Session 24704 of user root
系统日志报错很明显,内存不足导致MySQL OOM,和业务反馈的故障时间刚好对应上。
4、疑问:系统内存 64G,实际才使用了 22G 多,怎么会发现生 OOM?
根据日志可以推测发生 OOM 时,MySQL 占用系统内存 aron-rss 为anon-rss:23078724kB,大约为 22G,系统内存64G,而used 却使用了 61G,本不该发生OOM却发生了,那么其他内存去哪了?
5、传统大页
进一步排查,在传统大页这里发现了问题,在这里,传统大页 Total 配置了 20000,free 也为 20000,说明配置了大页但没在使用,hugepagesize 为 2M,这一块预留的计算出来就是 40G 大页内存。
“大内存页”也称传统大页、大页内存等有助于 Linux 进行虚拟内存的管理,标准的内存页为 4KB,这里使用“大内存页”最大可以定义 1GB 的页面大小,在系统启动期间可以使用“大内存页”为应用程序预留一部分内存,这部分内存被占用且永远不会被交换出内存,它会一直保留在那里,直到改变配置。
系统配置了40G大页,MySQL却没有使用上,这 40G 大页内存是分配给谁的呢?查询一下:
[root@mysql ~]# /proc/sys/vm/hugetlb_shm_group
27
[root@mysql ~]# id 27
uid=27(mysql) gid=27(mysql) groups=27(mysql)
hugetlb_shm_group 文件里显示指定大页内存使用的用户组 id,也就是MySQL用户,那既然是给 MySQL 的为什么一点没用上呢?
这是因为 MySQL 中还有专门启用大内存页的参数,在 MySQL 大内存页称为 large page(有兴趣的同学可以研究一下这个参数)。
6、查看 MySQL 配置文件
[mysqld]
user=mysql
port=3306
character-set-server=utf8
collation-server=utf8_general_ci
#large-pages #处于被禁用状态
skip-external-locking
配置文件里 large-page 参数处于禁用状态。
后与业务小哥确认,很早之前确实启用过 MySQL 的 large page,不过后面禁用了。排查到这基本就有了结论。
四、结论宕机原因:
操作系统层面开启了 20000 的大内存页,占用了 40G 内存空间给 MySQL 使用,并且 MySQL 也开启了 large page,到这一步都没有问题;但后来不使用的时候,只关闭了 MySQL 端的 large page 参数,没有更改主机关于大内存页的配置,所以导致主机上还存在 20000 的大内存页没在使用,这一部分内存长期空闲,并且其他程序不能使用。
所以 MySQL 在使用 22G(40G+22G=62G)内存左右,整个主机内存就趋于饱和了,这会导致MySQL运行不稳定,极端条件下就触发了 OOM,导致 mysqld 被 kill。整改释放被占用的内存,后续MySQL 就没再 OOM 过。建议
- MySQL 主机一般不必使用 hugepage,MySQL 自己处理 buffer pool 的分页管理,不需要操作系统的参与;
- 建议在环境上线前,检查一下主机内存的大内存页分配,看是否有没在使用的传统大页的存在,避免影响后续业务的使用。
觉得本文有用,请转发、点赞或点击“在看”聚焦技术与人文,分享干货,共同成长更多内容请关注“数据与人”