首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >MySQL里藏着一个会自己长大的文件,90%的DBA都忽略了

MySQL里藏着一个会自己长大的文件,90%的DBA都忽略了

作者头像
俊才
发布2026-01-07 17:54:04
发布2026-01-07 17:54:04
1490
举报
文章被收录于专栏:数据库干货铺数据库干货铺

那是一个再普通不过的周三下午,阳光透过落地窗在工位上投下斑驳的光影。我正慢悠悠的等下班——突然,手机“叮”地一声炸响,一条严重告警:“磁盘使用率超98%告警”。

1. 【紧急告警】测试服务器磁盘使用率 98%!

整个办公室瞬间安静了一秒,接着炸开了锅。 “不是上周才清理过日志吗?怎么又满了?” “是不是谁又跑了个全量导出?” “快查查是不是有大文件!”

DBA同事手忙脚乱地登录服务器,开始 du -sh * 扫描目录。我也悄悄 SSH 进了 MySQL 的数据目录,想看看有没有异常。

然后,我看到了它——192G的文件

代码语言:javascript
复制
-rw-r----- 1 mysql mysql 192G Aug 12 16:20 ibtmp1
192GB!

一个名字平平无奇、却如黑洞般吞噬磁盘空间的文件——ibtmp1。

2. 这个“幽灵文件”到底是什么?

在 MySQL 5.7 及之后版本中,ibtmp1 是 InnoDB 引擎用于存储临时表的独立表空间文件。默认配置是:

代码语言:javascript
复制
innodb_temp_data_file_path = ibtmp1:12M:autoextend

意思是:初始大小12MB,自动扩展,理论上可以无限增长(只要磁盘还有空间),而问题就出在这“无限”二字上。

而本次问题的起因也是因为测试环境没人关注性能问题(只关注数据库是否可用),一旦有低效 SQL 频繁创建临时表,ibtmp1 就会像吹气球一样膨胀——直到撑爆整个磁盘。

3. 如何“驱魔”?三步搞定

第一步:优雅停服,清空幽灵

临时表空间只在 MySQL 运行时存在。只要重启实例,ibtmp1 就会自动清零。但为了数据安全,建议先设置:

代码语言:javascript
复制
SET GLOBAL innodb_fast_shutdown = 0;
SHUTDOWN;

这样能确保 InnoDB 做一次完整的 checkpoint,避免意外。

注:MySQL 5.7+ 支持直接在 SQL 中执行 SHUTDOWN。

关机后,那个 192G 的“幽灵”果然消失了。

第二步:设上限,防复发

光清空不够,得治本。我们在 my.cnf 中加上限制:

innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:20G

现在,哪怕再有疯狂 SQL,最多也只能吃掉 20GB —— 超了就报错,反而能暴露问题。

重启 MySQL 后验证:

代码语言:javascript
复制
SHOW VARIABLES LIKE 'innodb_temp_data_file_path';
-- 输出:ibtmp1:12M:autoextend:max:20G 

第三步:揪出“真凶”——那些制造临时表的 SQL

为什么会有这么大临时表?得看哪些查询触发了 Using Temporary。

通过查询慢SQL,并查看执行计划,我们发现以下几种情况最容易“召唤幽灵”:

  • GROUP BY 用到了无索引字段
代码语言:javascript
复制
EXPLAIN SELECT * FROM test_tmp1 GROUP BY col2;
-- Extra: Using temporary; Using filesort
  • GROUP BY 和 ORDER BY 字段不一致

即使都有索引,也会用临时表:

代码语言:javascript
复制
EXPLAIN SELECT name FROM test_tmp1 GROUP BY name ORDER BY id DESC;
-- Extra: Using index for group-by; Using temporary; Using filesort
  • DISTINCT + ORDER BY 字段不匹配
代码语言:javascript
复制
EXPLAIN SELECT DISTINCT col2 FROM test_tmp1 ORDER BY name;
-- Extra: Using temporary; Using filesort
  • UNION(注意:UNION ALL 已优化,不使用临时表)
代码语言:javascript
复制
EXPLAIN SELECT name FROM t1 UNION SELECT name FROM t1 WHERE id < 10;
-- 出现 UNION RESULT 行,Extra: Using temporary
  • INSERT INTO ... SELECT ...

尤其是大表自复制:

代码语言:javascript
复制
INSERT INTO test_tmp3 SELECT * FROM test_tmp3;
-- Extra: Using temporary

💡 小结: 几乎所有用到临时表的场景,都是慢查询的高危信号。 ibtmp1 暴涨,其实是数据库在向你求救:“快优化这些烂 SQL!”

4. 模拟复现:亲眼见证 ibtmp1 膨胀

4.1 模拟使用临时表空间的场景

为了验证,我们做了个小实验:

  • 创建一张 1500 万行的大表 test_tmp3(无主键、无索引);
  • 设置 tmp_table_size = 16M,ibtmp1:12M:autoextend:max:2G, 逼 MySQL 使用磁盘临时表;
  • 执行 INSERT INTO test_tmp3 SELECT * FROM test_tmp3;

结果:

  • 初始 ibtmp1:12MB
  • 执行后:2.8GB
  • 再执行一次(上限设为 2G):报错!
代码语言:javascript
复制
ERROR 1114 (HY000): The table '/tmp/#sql_xxx' is full

日志里也记了一笔:

代码语言:javascript
复制
[ERROR] The table '/tmp/#sql_xxx' is full

这正是我们想要的“熔断机制”——宁可让 SQL 失败,也不能让服务器宕机。

4.2 相关参数

除了 innodb_temp_data_file_path,还有几个参数会影响临时表行为:

  • tmp_table_size:内存临时表最大值
  • max_heap_table_size:用户定义 MEMORY 表的上限
  • default_tmp_storage_engine:临时表默认引擎(5.7+ 默认 InnoDB)
  • internal_tmp_disk_storage_engine:磁盘临时表引擎(8.0+ 引入)

它们相互影响,共同决定临时表是走内存还是磁盘。

5 尾声:幽灵退散,系统重生

那天晚上,我们不仅清掉了 192G 的“幽灵”,还顺藤摸瓜干掉了三个低效查询。从此,测试环境再没因 ibtmp1 报警。而我也明白了一个道理:数据库不会无缘无故变慢,也不会无缘无故吃光磁盘。它每一次“异常”,都是在用沉默的方式,讲述一个又一个被忽视的故事。

下次当你看到 ibtmp1 异常膨胀,请别急着删文件——先问问:是谁,在背后写下了那行“可怕”的 SQL?

大家平时还遇到哪些问题,也欢迎留言交流。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据库干货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档