前言:最近生产环境新上了一台 MySQL 服务器,在运行一段时间后发现
  • buff/cache 总是异常增长(一天增长 2 至 3 G)

  • MySQL 客户端时常会出现断开连接情况:

2013, ‘Lost connection to MySQL server during query’

2006, ‘MySQL server has gone away’


基础环境:

[root@mysql-redis ~]# hostnamectl
   Static hostname: mysql-redis
         Icon name: computer-vm
           Chassis: vm
        Machine ID: b62ee62249064ae3850030f860d1c36f
           Boot ID: 76b979281b9247e794105d40bc18cf7b
    Virtualization: vmware
  Operating System: CentOS Linux 7 (Core)
       CPE OS Name: cpe:/o:centos:centos:7
            Kernel: Linux 3.10.0-862.3.2.el7.x86_64
      Architecture: x86-64

[root@mysql-redis ~]# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 323
Server version: 8.0.15 MySQL Community Server - GPL

首先是用 free -h 查看内存信息

[root@mysql-redis ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:           7.6G        1.7G        3.5G         55M        2.4G        5.6G
Swap:          3.0G        2.8M        3.0G

使用 atop 查看一下内存分配

发现buff/cache的占用几乎全部为 Cache 使用(Buff为写入缓存;Cache 为读取缓存)

继续使用 slabtop 查看到底是那些使用了 Cache

上图可以发现proc_inode_cache 使用量一骑绝乘,遥遥领先

proc_inode_cache 代表 proc 文件系统的 inode 占用

使用 ps -e 发现进程不多;进一步使用 ps -eLf 查看线程

[root@mysql-redis ~]# ps -eLf
#----
#上面输出省略
#----
mysql    21659     1 21659  0  106 4月22 ?       00:00:16 /usr/sbin/mysqld
mysql    21659     1 21664  0  106 4月22 ?       00:01:29 /usr/sbin/mysqld
mysql    21659     1 21665  0  106 4月22 ?       00:01:29 /usr/sbin/mysqld
mysql    21659     1 21666  0  106 4月22 ?       00:01:34 /usr/sbin/mysqld
mysql    21659     1 21667  0  106 4月22 ?       00:01:33 /usr/sbin/mysqld
mysql    21659     1 21668  0  106 4月22 ?       00:01:36 /usr/sbin/mysqld
mysql    21659     1 21669  0  106 4月22 ?       00:01:35 /usr/sbin/mysqld
mysql    21659     1 21670  0  106 4月22 ?       00:01:46 /usr/sbin/mysqld
mysql    21659     1 21671  0  106 4月22 ?       00:01:35 /usr/sbin/mysqld
mysql    21659     1 21672  0  106 4月22 ?       00:01:32 /usr/sbin/mysqld
#----
#下面面输出省略
#----

发现了一百多个 MySQL 线程

👇计算一下 Socket

[root@mysql-redis ~]# ll /proc/21659/task/*/fd/ |grep socket |wc -l
6148

还在可接受返回之内

👇计算一下 fd

[root@mysql-redis ~]# ll /proc/21659/task/*/fd/ |wc -l
711895

这个就很操蛋了,接受不能啊!

再来看看到底都是啥玩意儿占用了这么多 fd

[root@mysql-redis ~]# ll /proc/21659/task/*/fd/
/proc/21659/task/1128/fd/:
总用量 0
lr-x------ 1 mysql mysql 64 5月   8 18:15 0 -> /dev/null
l-wx------ 1 mysql mysql 64 5月   8 18:15 1 -> /var/log/mysqld.log
lrwx------ 1 mysql mysql 64 5月   8 18:15 10 -> /db_apps/mysql/ibdata1
lrwx------ 1 mysql mysql 64 5月   8 18:15 100 -> /db_apps/mysql/rpa_management/rpa_api_group.MYD
lrwx------ 1 mysql mysql 64 5月   8 18:15 1000 -> /tmp/mysql_temptable.yes57Z (deleted)
lrwx------ 1 mysql mysql 64 5月   8 18:15 1001 -> /tmp/mysql_temptable.EWyyWI (deleted)
lrwx------ 1 mysql mysql 64 5月   8 18:15 1002 -> /tmp/mysql_temptable.z0Xs8A (deleted)
lrwx------ 1 mysql mysql 64 5月   8 18:15 1003 -> /tmp/mysql_temptable.26ywwO (deleted)

#----
#👇输出省略 多得吓死个人
#----

每个inode都是占用空间的,且proc文件系统是全内存的。 所以我们才会看到slab中proc_inode_cache内存占用高

解决办法:

在我经过各种 Google 之后发现了 [有人和我出现了似乎相同的情况](https://dba.stackexchange.com/questions/228403/mysql-crashing-at-about-70k-open-files

里面的回答提醒了我:

The issue is a Mysql bug (https://bugs.mysql.com/bug.php?id=94185 )

于是乎 我去翻看了 MySQL 8.0.16 的 [Change Log](https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-16.html

我发现了以下两个 fixs bug:

  1. InnoDB: Optimized InnoDB internal temporary tables did not support in-place UPDATE operations, which caused the number of delete-marked records to increase continuously. The large number of delete-marked records could cause longer than expected query execution times. (Bug #29207450)

  2. InnoDB: The purge thread failed to free LOB data pages. (Bug #28510599)

  1. inodb临时表 不能原地 update,会导致删除标记(也就是 mysql_temptable.26ywwO (deleted) )记录数量源源不断的增加。大量删除标记的记录会导致查询时间超过预期(这也是为什么客户端会出现各种报错的原因)!

  2. 清除线程无法释放 LOB 数据页(这也是 Cache 会源源不断增加的原因)

解决方式:将 MySQL 从 8.0.15 升级到 8.0.16

  1. 防止意外先备份数据库:
   [root@mysql-redis ~]# mysqldump -uxxxx -pxxxxxx --all-databases --add-drop-table --add-locks > dump.sqlbash
  1. 升级 MYSQL
   [root@mysql-redis ~]# yum update mysql-server

在 Yum 更新完成之后, 启动MySQL,在 8.0.16 之后不需要执行 mysql_upgrade, 会自动处理。

5cd39a21cb424

以上处理完毕