前几天业务线发现一例MySQL数据库内存增长异常案例,现象如下:
数据库版本:Percona MySQL 5.0.67
数据量:670M+
内存大小:16G
Innodb Buffer Pool Size:4G
其它回话内存分配并不大,基本在100M以内,并且连接数非常少
服务器负载不高,开启Binlog,数据库和程序都已经进行过迁移,问题仍然存在。通过top命令查看MySQL占用内存一直在增长,且开始使用Swap。
分析排除过程:
1.怀疑到的是InnoDB内存占用,使用show engine innodb status查看,发现Free Buffer占很大部分,因此可以判断分配给Innodb引擎的内存够用
2.查看回话,回话数基本在3~4个,且回话分配的内存大小与现在MySQL占用的回话总和相差很大,因此也不会是回话的问题
3.程序、数据库软件问题。已经迁移过两次,版本与外网一致,问题依旧,因此也排除了这个问题
4.MySQL重启后内存释放,因此问题还是在MySQL身上,根据网上说的Flush Tables的做法发现无效,所以也不是打开的表数量过大的问题导致。
就在临时维护前1小时,网上查找案例时,有人说内存表配置不当也有可能导致内存异常,但因为天龙没有用到Memory引擎,因此没太在意,但已经山穷水尽,也不知道该做什么,就查了下数据库中Innodb以外的表,主要集中在mysql库及information_schema库:
+——————–+—————————————+——–+
| TABLE_SCHEMA | TABLE_NAME | ENGINE |
+——————–+—————————————+——–+
| information_schema | CHARACTER_SETS | MEMORY |
| information_schema | CLIENT_STATISTICS | MEMORY |
| information_schema | COLLATIONS | MEMORY |
| information_schema | COLLATION_CHARACTER_SET_APPLICABILITY | MEMORY |
| information_schema | COLUMNS | MyISAM |
……
| mysql | help_topic | MyISAM |
| mysql | time_zone | MyISAM |
| mysql | time_zone_leap_second | MyISAM |
| mysql | time_zone_name | MyISAM |
| mysql | time_zone_transition | MyISAM |
| mysql | time_zone_transition_type | MyISAM |
| mysql | user | MyISAM |
+——————–+—————————————+——–+
因为MyISAM表不多,而且表都不大,内存分配也没有问题,所以我大概过了一遍,剩下的就是MEMORY 表了。
单独对这些表进行查看,最终发现CLIENT_STATISTICS表非常异常,原因是作为内存表查询速度非常慢,,并且会报错,在/tmp/下的一个临时文件需要修复,因此到/tmp目录下进行查看,基本就定位到问题了:导出的临时表约为7~8G!
查看表数据基本不可能,因此在维护时,重启了数据库,并且查看了里面的数据,发现数据增长非常快,里面记录的都是客户端的连接记录,比较特别的就是主机名显示不全。因此我们选择将主机名改成IP的形式,用于定位出问题的服务器(当时还在怀疑是程序连接中断导致记录数增长)。但修改完后发现问题消失了,该台服务器与其他服务器区别,问题表的数据量也正常了,因此我们定位问题到了主机名上,我进行了一下实验:
现在有245、246两台机器:
246是客户端、245是服务器端
(root:245:Thu Apr 24 16:37:28 2014)[information_schema]> select count(*) from CLIENT_STATISTICS;
+———-+
| count(*) |
+———-+
| 2 |
+———-+
1 row in set (0.00 sec)
可以看到表里面的数据是2条
[dbatlbb@ ~]$ mysql -h 245 -uwuwl_test -p
mysql: Can’t create/write to file ‘/home/mysql/query.log’ (Errcode: 13)
Error logging to file ‘/home/mysql/query.log’
Logging to file ‘/home/dbatlbb/mysql/query.log’
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 556
Server version: 5.0.67-percona-highperf-log Source distribution
Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the buffer.
(wuwl_test:${HOSTNAME}:Thu Apr 24 16:39:19 2014)[(none)]> show processlist;
+—–+———–+———————————+——+———+——+——-+——————+
| Id | User | Host | db | Command | Time | State | Info |
+—–+———–+———————————+——+———+——+——-+——————+
| 556 | wuwl_test | gs_438_friuha_fgesihs_fsd:50768 | NULL | Query | 0 | NULL | show processlist |
+—–+———–+———————————+——+———+——+——-+——————+
1 row in set (0.00 sec)
连接了一下,并进行了一次查询操作
(root:NJ-245:Thu Apr 24 16:38:38 2014)[information_schema]> select count(*) from CLIENT_STATISTICS;
+———-+
| count(*) |
+———-+
| 5 |
+———-+
1 row in set (0.01 sec)
发现数据有增长,再次进行几次查询,发现每次查询都会有增长,这个正常现象么?我修改了下主机名进行了再次查询,发现表数据不再增长。
修改方法:主机名需要小于16字节。
目前在5.5和5.6版本上进行了测试,发现这个表数据为空,因此这个问题可能是在5.0版本上面的一个BUG。
总结:
本次问题现象主要是:内存异常被MySQL占用不释放并持续上涨,更换过程序和数据库服务器仍没有效果,服务器数据量很小,Innodb Buffer空闲很大。
问题原因:由于数据库自身的统计数据表information_schema.CLIENT_STATISTICS 数据量过大导致内存占用,CLIENT_STATISTICS是一张内存表。
问题处理方法:由于CLIENT_STATISTICS表里面的Host列是16个字节的,因此如果主机名过长,会导致客户端每一次的操作都会在表里面新增一条数据,无论操作是否成功。因此只需要修改连接的主机名不要超过16位就不会导致内存占用不释放的问题。
思考:修改了默认的东西,需要在小部分服务器上进行测试,并详细记录修改后的异常,用于对比不同服务器之间的差别。
延伸:
CLIENT_STATISTICS 用于统计客户端连接,目前5.5、5.6以及Maria已经将client字段升级为64个字节,且该统计功能默认关闭,所以如果大家有统计需要,一定要记得主机名不能大于64个字节!
--转自