临时表创建/删除是隐式提交的ddl操作,必然触发磁盘io;应复用结构、清空数据而非频繁重建,优先使用表变量或cte替代。

临时表创建/删除本身就会触发磁盘写入
不是“可能”IO高,而是DROP TABLE #t和CREATE TABLE #t这两个操作在 SQL Server 和 MySQL 中都是**隐式提交的 DDL 操作**——它们不等事务结束,执行完立刻落盘。这意味着每次建表、删表,都要写 tempdb 的数据文件(SQL Server)或 ibtmp1 + 系统表(MySQL),哪怕表是空的。
常见错误现象:sp_who2 或 sys.dm_exec_requests 显示大量会话卡在 WRITELOG 或 IO_COMPLETION 等待;sys.dm_io_virtual_file_stats 中 tempdb 数据文件的 num_of_writes 暴涨。
- SQL Server:全局临时表
##t存在时,所有会话共享同一结构,但删它仍要更新tempdb.sys.sysobjvalues等系统表页,PFS/GAM 争用直接体现为 IO 延迟 - MySQL:
CREATE TEMPORARY TABLE t(...)默认先尝试 MEMORY 引擎,但只要字段含TEXT/BLOB或行长度超max_heap_table_size,就强制落地磁盘 MyISAM 临时表——删表时同样要同步清理磁盘文件和 frm 文件
频繁 DDL 会拖垮 tempdb / ibtmp1 的元数据性能
临时表不是“用完就扔”的轻量对象。每次创建都要在系统目录中插入记录、分配 IAM 页、初始化统计信息;删除则要反向清理这些元数据。高并发下,这些操作会竞争 tempdb 的系统表(如 sysallocunits)或 MySQL 的 INFORMATION_SCHEMA 缓存锁。
使用场景:存储过程被每秒调用数百次,内部都含 SELECT ... INTO #tmp → 实际不是数据大导致慢,而是每秒几百次元数据修改把 tempdb 的 SGAM 页锁死了。
- SQL Server 可查:
SELECT * FROM sys.dm_os_waiting_tasks WHERE wait_type LIKE 'SOS_%',若大量SOS_SCHEDULER_YIELD+PAGELATCH_UPon PFS pages,基本锁定是 tempdb 元数据争用 - MySQL 可查:
SHOW GLOBAL STATUS LIKE 'Created_tmp%',如果Created_tmp_disk_tables每秒增长 >5,且Created_tmp_tables同步飙升,说明 DDL 频率已超出内存缓冲能力
本地临时表 #t 比全局 ##t 更安全,但不能滥用
很多人以为换用 #t 就能避开问题,其实只是把压力从跨会话争用转为单会话内反复刷脏页。尤其当存储过程中嵌套调用、循环建表时,#t 的生命周期管理反而更难控制。
容易踩的坑:
-
TRUNCATE TABLE #t比DROP TABLE #t快,但它不释放分配的区(extent),多次循环后 tempdb 空间碎片化严重,后续建表更容易触发磁盘 IO - 在循环里写
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t; CREATE TABLE #t (...)—— 这等于每轮都做一次完整 DDL,比直接TRUNCATE多出 3 倍以上元数据开销 - MySQL 中
CREATE TEMPORARY TABLE后没显式DROP,会话异常断开时由服务端异步清理,期间残留的 .MYD/.MYI 文件仍占 IO 资源
真正省 IO 的做法不是“删得快”,而是“别删”
优化方向从来不是调优 DROP 语句,而是绕过 DDL。核心逻辑:临时表结构稳定就复用,数据变动就清空,彻底避免重建。
实操建议:
- SQL Server:用
IF NOT EXISTS (SELECT 1 FROM tempdb.sys.tables WHERE name LIKE '#t%') CREATE TABLE #t (...)+TRUNCATE TABLE #t,确保结构只建一次 - MySQL:改用
CREATE TEMPORARY TABLE IF NOT EXISTS t (...) ENGINE=MEMORY,并提前调大max_heap_table_size(注意单位是字节,不是 MB) - 两者都优先考虑表变量:
DECLARE @t TABLE(...)(SQL Server)或 CTE 替代简单中间结果(MySQL 8.0+)——它们不走 tempdb,纯内存操作
最常被忽略的一点:很多“必须用临时表”的逻辑,其实是因为 JOIN 条件没索引、ORDER BY 字段缺失覆盖索引,才被迫用临时表兜底。先看 EXPLAIN 或执行计划里的 Warning: Using temporary,比优化临时表本身更治本。











