经典案例 | 记一次MySQL OOM处理过程

记一次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 disabled2021-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 available2021-08-26T13:43:42.313160Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins2021-08-26T13:43:42.313170Z 0 [Note] InnoDB: Uses event mutexes2021-08-26T13:43:42.313173Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier2021-08-26T13:43:42.313203Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.112021-08-26T13:43:42.313209Z 0 [Note] InnoDB: Using Linux native AIO2021-08-26T13:43:42.329628Z 0 [Note] InnoDB: Number of pools: 12021-08-26T13:43:42.357426Z 0 [Note] InnoDB: Using CPU crc32 instructions2021-08-26T13:43:42.377711Z 0 [Note] InnoDB: Initializing buffer pool, total size = 20480M, instances = 12021-08-26T13:43:42.562303Z 0 [Note] InnoDB: Completed initialization of buffer pool2021-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 tables2021-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 3230293872021-08-26T13:43:43.422345Z 0 [Note] InnoDB: Buffer pool(s) load completed at 210826 21:43:432021-08-26T13:43:43.527409Z 0 [Note] Server hostname (bind-address): '*'; port: 33062021-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 kBMemFree:          2097152 kBBuffers:            21160 kBCached:           4996040 kB

3、分析最近一次异常期间操作系统日志

因为内存使用率实在太高,怀疑mysqld进程被kill,所以查看下系统日志,发现了重启原因。

master kernel:Out of memory:Kill process 1482 (mysqld) score 351 or sacrifice childmaster kernel:Killd process 1482 total-vm:65108800kB,anon-rss:23078724kB,file-rss:0kBmaster 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_group27
[root@mysql ~]# id 27uid=27(mysql) gid=27(mysql) groups=27(mysql)

hugetlb_shm_group 文件里显示指定大页内存使用的用户组 id,也就是MySQL用户,那既然是给 MySQL 的为什么一点没用上呢?

这是因为 MySQL 中还有专门启用大内存页的参数,在 MySQL 大内存页称为 large page(有兴趣的同学可以研究一下这个参数)。

6、查看 MySQL 配置文件

[mysqld]user=mysqlport=3306character-set-server=utf8collation-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 的分页管理,不需要操作系统的参与;
  • 建议在环境上线前,检查一下主机内存的大内存页分配,看是否有没在使用的传统大页的存在,避免影响后续业务的使用。


觉得本文有用,请转发、点赞或点击“在看”聚焦技术与人文,分享干货,共同成长更多内容请关注“数据与人”

为您推荐

发表评论

您的电子邮箱地址不会被公开。