应先估算数据量再决定是否建临时表:超10万行改用表变量或分批处理;避免非必要索引,优先聚簇索引;表变量适用于小数据量但慎用于复杂查询;通过dmv定位tempdb高占用会话及语句,排查未提交事务和pfs页争用。

临时表写入前先估算数据量
很多存储过程在循环或大结果集上直接 SELECT INTO #temp,没做任何预判。一旦源表有百万行、带 ORDER BY 或 GROUP BY,SQL Server 就可能把整个排序中间结果塞进 tempdb,瞬间占满空间。
实操建议:
- 用
SELECT COUNT(*)或sys.dm_db_partition_stats先查源数据行数,超 10 万就别硬建临时表,改用表变量或分批处理 - 避免在临时表上建非必要索引——
CREATE INDEX本身也走 tempdb,尤其SORT_IN_TEMPDB = ON时更猛 - 如果必须建索引,优先用
CLUSTERED(只存一份数据),少用NONCLUSTERED(额外维护 B 树结构)
用表变量替代小规模临时表
DECLARE @t TABLE 在 SQL Server 中默认走内存,不记日志,也不触发 tempdb 分配页争用。但注意它不是万能的:超过几千行、需要频繁 JOIN 或 WHERE 过滤时,优化器可能放弃统计信息,生成低效执行计划。
实操建议:
- 数据量 @t
- 需要主键或唯一约束 → 加
PRIMARY KEY或UNIQUE,否则缺失索引会拖慢后续操作 - 不要在表变量里用
SELECT * INTO @t(语法错误),必须显式声明列
显式清理 + 作用域最小化
临时表不会自动释放,直到会话断开或显式 DROP TABLE。而存储过程中若嵌套调用、异常退出、或用了 GO 批处理分隔符,#temp 可能残留并持续占空间。
实操建议:
- 每个
CREATE TABLE #t后,对应位置加DROP TABLE IF EXISTS #t,别依赖“自然结束” - 避免在
TRY...CATCH外建临时表;若在TRY内建,CATCH块末尾必须DROP - 不用
SELECT INTO创建临时表——它绕过元数据缓存,每次都会重建分配结构,加剧 PFS/SGAM 争用
监控和定位真实瓶颈点
tempdb 溢出常被误判为“临时表太多”,其实可能是某条 ORDER BY 查询没走索引,或者一个未提交事务锁住了大量行版本,导致 sys.dm_db_file_space_usage 显示大量 VERSION_STORE 占用。
实操建议:
- 运行
SELECT * FROM sys.dm_db_session_space_usage WHERE session_id > 50 ORDER BY user_objects_alloc_page_count DESC,找真实吃空间的会话 - 对高 alloc 值会话,再查
sys.dm_exec_requests和sys.dm_exec_sql_text定位具体语句 - 检查是否有长时间运行的显式事务:
DBCC OPENTRAN('tempdb'),这类事务会让行版本无法清理
真正卡住 tempdb 的,往往不是那几行 CREATE TABLE #t,而是排序缓冲区不足、事务未及时提交、或多个会话同时高频创建删除小临时表引发的 PFS 页争用——这些细节不盯 DMV,光删表根本治标不治本。











