vlambda博客
学习文章列表

MySQL查询排序竟然把磁盘撑爆了

须弥零一

MySQL查询排序竟然把磁盘撑爆了

今天这篇文章主要记录一下今天发现的一个现象,以及如何来解决这个问题。但是引起该问题的根因分析,因为今天太晚了,后期会再跟进补充。

问题描述

先说说是什么问题吧。线上环境有个接口特别耗时,今天刚好有时间进行分析并调优一下,但是在修改SQL执行的时候冷不丁出现了下面这样的报错:
查询:SELECT t1.id AS xjob_id, t1.parent_id, t1.order_number,t3.path_name, t1.name AS xjob_name, t1.project_id , t2.id AS relation_id,...错误代码: 126Incorrect key file for table '/tmp/#sql_1_1.MYI'; try to repair it

求助Baidu

这个错误信息我也是第一次看见,看错误信息的字面意思还真看不出来是个什么问题。于是百度之。
百度完之后得出了一个结论: 上面报错是因为 /tmp 目录所在的挂载区满了。
注:/tmp 的挂载点在 /
得到这个结论更是让我一头雾水,直接懵逼。因为报错SQL和没报错的SQL相比就多了一句  ORDER BY column1, column2。学海无涯,看来还是得多读书啊。

验证求助结果

但懵逼归懵逼,我得亲眼看见确实是因为是  ORDER BY 引起的磁盘满了才能进一步往下分析不是。于是我编写了如下脚本来监控SQL执行期间的增长情况:
!/bin/bashwhile truedo date '+%F %T.%N' df -h echo "" ls -lrt /tmp/#sql* sleep 0.2s echo ""done

1. 首先使用如下语句开启监控:

~: chmod +x ./test.sh~: ./test.sh > ./minitor.log &

2.然后执行SQL
     我是用 Webyog SQLyog 工具执行的。用什么工具执行不影响我们的观察结果,这里就不做展示了。

3. 等待SQL执行报错后结束脚本,打开上面monitor.log文件。

看到  monitor.log 的结果后这下实锤了,果然在报错之前  / 的磁盘使用率达到了**100%**,并且  /tmp/#sql_1_1.MYI 文件的大小也在一直增长。等到SQL执行报错后,这个文件会被自动删除,磁盘空间又恢复到了原状。

注:这里因为某些原因,就不把 monitor.log 中的信息放出来了。

解决问题

正面硬刚

既然知道是由于SQL的  ORDER BY 引起的,那就继续优化我们SQL。这个优化宗旨是:

尽量将排序的字段不要落到中间表上来解决通过WHERE子句尽可能多的过滤掉需要排序的数据量

具体怎么来优化操作还得看大家的业务了,这个没有一个通用的办法。

迂回战术

如果因为各种原因正面刚不过,那就只剩这个办法了: 修改临时目录路径,扩大容量。
使用如下SQL可以找到你的MySQL临时目录的配置:
mysql> show variables like '%tmpdir%';+-------------------+-------+| Variable_name | Value |+-------------------+-------+| innodb_tmpdir | || slave_load_tmpdir | /tmp || tmpdir | /tmp |+-------------------+-------+3 rows in set (0.00 sec)
如上, /tmp 目录就是本机MySQL的临时目录配置。
假如我们找到的一个空间更大挂载点目录是: /mnt/mysql_tmp

1. 命令行配置

执行如下SQL语句即可:
mysql> set global tmpdir = '/mnt/mysql_tmp';mysql> set global slave_load_tmpdir = '/mnt/mysql_tmp';

2. 配置文件配置

如果上述SQL语句执行失败,或您希望永久生效则需要修改 my.cnf 配置文件来完成配置。其方法为在  [mysqld] 的 option 下添加如下配置:
tmpdir=/mnt/mysql_tmpslave_load_tmpdir=/mnt/mysql_tmp
配置完成后保存,重启MySQL:
service mysql restart

3. 云上环境或虚拟机

如果您的服务器是云主机的话,这个用钱就可以解决,直接报工单扩容就行。
如果是自建虚拟机的话,同样给虚拟机磁盘扩容即可。

未完待续

因为到此,我还没有弄清楚为啥一个  ORDER BY 就能占用如此大的磁盘空间(这个例子中涨了30G)。
难不成MySQL排序的时候是将创建的中间表放在了这个目录?(肯定不会在内存中的,要是内存必然早就爆掉了)
今天太晚了,来不及深入寻求这个答案了,后面找时间把这一点内容再补上。有了解的朋友也可以留言交流哦,让我学习学习~ (●ˇ∀ˇ●)


---- END ----