MySQL大页内存导致OOM宕机

后期复现。业务反馈,数据库最近总是隔一段时间连接失败,过一会又没事了,一天能发生了 2、3 次,后来发现和主机传统大页的配置有关,具体原因是什么,请继续看。

一、先决条件

  • MySQL 5.7.30

  • vmware 虚拟机 CentOS 7.5

  • CPU 4C

  • 内存 4G

  • innodb_buffer_pool_size = 3G

二、环境复现

2.1 开启大页
  • 如果存在 echo 开启大页页数不准,请再次执行 echo 语句即可
# 设置大页内存,每一页的大小( 默认:2M ) 当前 2097152 就是 2M
[root@db ~]# echo 2097152 > /proc/sys/kernel/shmall
[root@db ~]# cat /proc/sys/kernel/shmall
2097152

# 设置使用的页数,每个页如果是 2MB ,所以总和就为 2000 MB.
[root@db ~]# echo 1000 > /proc/sys/vm/nr_hugepages
[root@db ~]# cat /proc/meminfo | grep -i huge
AnonHugePages:      4096 kB
HugePages_Total:    1000
HugePages_Free:     1000
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
2.2 授权用户
  • 设置访问大页内存的操作系统的用户组 ID ,mysql 用户必须在这个组内
[root@db ~]# id mysql
uid=1000(mysql) gid=1000(mysql) groups=1000(mysql)

[root@db ~]# echo 1000 > /proc/sys/vm/hugetlb\_shm\_group
[root@db ~]# cat /proc/sys/vm/hugetlb\_shm\_group
1000
2.3 查看内存
  • 此时大页内存已经占用了 2G 的内存空间,同时 MySQL 实例也分配了 2G Buffer 空间
[root@db ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        2.6G        1.0G         16M        116M        981M
Swap:            0B          0B          0B

三、故障复现

  • 分配了 3G Buffer 给 MySQL,不过不是立刻占用的,需要MySQL一点点使用才分配
3.1 模拟业务
  • 当前环境存在 4 张 200 万数据的表
[root@db ~]# sysbench /usr/share/sysbench/tests/include/oltp_legacy/oltp.lua --mysql-host=10.186.60.60 --mysql-port=3306 --mysql-user=root --mysql-password=123456 \
             --mysql-db=testdb --oltp-table-size=2000000 --oltp-tables-count=10 --threads=4 --events=0 --time=3600 --report-interval=3 run
3.2 数据库OOM
  • 服务器发生了 OOM,数据库宕机

  • 计算 rss 的数量( 此处为页数 ),每页为 4K,计算完成近大约为 1.6G。

  • 那么问题来了,

    • 主机内存 4G,实际才使用了 1.6G 多,怎么会发现生 OOM,free used 使用了 3.6G,那么我的内存去哪了?

3.3 大页内存
  • 在这里,传统大页 total 配置了 1000,free 也为 1000,说明配置了大页但没在使用,hugepagesize 为 2M,这一块预留的就是 2G 大页内存。
[root@db ~]# cat /proc/meminfo | grep -i huge
AnonHugePages:      4096 kB
HugePages_Total:    1000
HugePages_Free:     1000
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB

“大内存页” 也称传统大页、大页内存等有助于 Linux 进行虚拟内存的管理,标准的内存页为 4KB,这里使用“大内存页”最大可以定义 1GB 的页面大小,在系统启动期间可以使用“大内存页”为应用程序预留一部分内存,这部分内存被占用且永远不会被交换出内存,它会一直保留在那里,直到改变配置。

3.4 大页内存分配谁?
  • hugetlb_shm_group 文件里填的是指定大页内存使用的 用户组 id,这里查看到是 MySQL 组 id,那既然是给 MySQL 的为什么 free 等于 total,并且 mysql 还只有 1.6G 实际使用内存呢?
[root@db ~]# cat /proc/sys/vm/hugetlb_shm_group
1000

[root@db ~]# id 1000
uid=1000(mysql) gid=1000(mysql) groups=1000(mysql)

后与业务确认,很早之前确实启用过 mysql 的 large page,不过后面禁用了。排查到这基本就有了结论。

3.5 结论
  • 这套环境之前开启了 1000 的大内存页,每页大小为 2MB,占用了 2G 内存空间,给 MySQL 使用,并且 MySQL 开启了 large page,但后来不使用的时候,只关闭了 MySQL 端的 large page 参数,但没有实际更改主机的关于大内存页的配置,所以导致,实际上主机上的还存在 1000 的大内存页,并且没在使用,这一部分长期空闲,并且其他程序不能使用。

  • 所以 MySQL 在使用 1.6G 内存左右,整个主机内存就饱和了,然后在部分条件下,就触发了 OOM,导致 mysqld 被 kill,但主机上又有 mysqld_safe 守护程序,所以又再次给拉起来,就看到了文章初的偶尔连接不上的现象。

解决:
经过在本地测试,确实指定大页之后会导致内存占用,如果 MySQL 不配置,会空闲这部分内存,且模拟大业务的情况下会发生 OOM。

所以,在问题环境上:

通过移除 vm 相关参数,使被占用的大内存页释放出来,MySQL 就没再被 oom 过。

注意:/etc/rc.local 、/etc/sysctl.conf 两个文件检查下,避免服务器重启后再次启动 大页内存

  • [root@db ~]# echo 0 > /proc/sys/vm/nr_hugepages

建议:

1、MySQL 主机一般不必使用 hugepage,MySQL 自己处理 buffer pool 的分页管理,不需要操作系统的参与;

2、建议在环境上线前,检查一下主机内存的大内存页分配,看是否有没在使用的传统大页的存在,避免影响后续业务的使用。

大页内存参考链接:https://kerneltalks.com/services/what-is-huge-pages-in-linux/

  • 其他玩意⬇️
[root@db ~]# yum -y install numactl
[root@db ~]# numactl --hardware
available: 1 nodes (0)
node 0 cpus: 0 1 2 3
node 0 size: 4095 MB
node 0 free: 803 MB
node distances:
node   0 
  0:  10
「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论