MySQL 临时表是什么?内部临时表和用户临时表有什么区别?
简化版
MySQL 临时表分为用户显式创建的临时表和优化器执行查询时产生的内部临时表。用户临时表用 CREATE TEMPORARY TABLE 创建,只在当前连接可见,连接关闭后自动删除。内部临时表常出现在排序、分组、去重、UNION、复杂子查询等场景,可能在内存中,也可能落盘,落盘后性能会明显下降。
详细版
用户临时表适合在一个会话中保存中间结果,比如复杂批处理或报表分步计算。它不会和其他连接同名表冲突,也不会被其他连接访问。
内部临时表是 MySQL 为了执行 SQL 自动创建的,开发者看不到创建语句,但可以从执行计划和状态指标中观察。GROUP BY、ORDER BY、DISTINCT、窗口函数、派生表等都可能触发内部临时表。
面试中要重点说明:临时表不是一定慢,真正要关注的是是否落盘、数据量大小和是否可以通过索引或 SQL 改写减少临时表。
完整版教学
一、用户临时表
用户临时表由开发者显式创建。
它只在当前连接中可见。
连接断开后会自动删除。
同名临时表可以遮蔽普通表。
这点在调试和脚本中要小心。
二、用户临时表示例
CREATE TEMPORARY TABLE tmp_user_ids (
user_id BIGINT PRIMARY KEY
);
INSERT INTO tmp_user_ids VALUES (1), (2), (3);
这类表适合保存会话级中间结果。
但连接池环境下要注意连接复用和清理。
三、内部临时表
内部临时表由优化器自动创建。
开发者不能直接命名它。
它用于保存排序、分组、去重或派生结果。
如果数据量小,可能在内存中完成。
如果数据量大或包含不适合内存的字段,可能落盘。
四、常见触发场景
| 场景 | 为什么可能需要临时表 | 优化方向 |
|---|---|---|
GROUP BY | 保存分组中间结果 | 合适索引、减少数据量 |
ORDER BY | 排序结果无法直接用索引 | 设计排序索引 |
DISTINCT | 去重需要中间集合 | 避免不必要去重 |
UNION | 默认去重 | 可用 UNION ALL |
| 派生表 | 子查询物化 | 改写 SQL 或加索引 |
五、如何观察
EXPLAIN 中可能看到 Using temporary。
状态变量可以观察创建临时表数量。
慢查询日志也能辅助定位。
如果 Created_tmp_disk_tables 很高,要关注落盘临时表。
落盘通常意味着更多 IO 和更差响应时间。
看到临时表不要立刻恐慌,要先判断是内存临时表还是磁盘临时表,以及数据量是否可控。
六、连接池里的坑
用户临时表绑定连接。
如果连接池复用连接,临时表可能在逻辑请求之间残留。
虽然连接关闭会清理,但连接池不一定真的关闭物理连接。
因此脚本要显式 DROP 或使用可靠命名。
业务在线请求中不建议滥用用户临时表。
七、误区和追问
- 误区:临时表都是开发者创建的。 MySQL 执行复杂查询时也会产生内部临时表。
- 误区:Using temporary 一定代表 SQL 不可接受。 小数据量或必要场景可以接受,关键看成本。
- 误区:连接关闭前临时表一定马上消失。 连接池复用时,物理连接可能没关闭。
- 追问:如何减少内部临时表? 通过合适索引、减少排序分组数据、避免不必要 DISTINCT 或改写 SQL。
- 追问:UNION 为什么可能产生临时表?
UNION默认去重,需要保存中间结果;UNION ALL不去重。 - 追问:磁盘临时表为什么慢? 它涉及更多磁盘 IO 和中间结果写读成本。
八、面试收束
回答时先分用户临时表和内部临时表。
再讲可见性、生命周期、触发场景和落盘风险。
最后补充连接池和执行计划观察。