临时表未建索引、大排序未限流、事务未提交三者叠加必然导致tempdb空间分配失败,报错1105/3959等,并非磁盘真满而是内存授予不足引发页分配卡死。

临时表没加索引、大排序没限流、事务不提交——这三件事凑一块,tempdb 几乎必爆。不是磁盘真满了,而是 SQL Server 在分配页时卡死,报错如 1105、3959 或 Could not allocate space for object 'dbo.#temp_table' in database 'tempdb'。
临时表建完就加索引,别等 JOIN 时才想起来
显式创建空临时表后立刻建索引,比 SELECT INTO #temp 安全得多。后者不带索引、无统计信息、优化器常低估行数,导致内存授予不足,后续排序或哈希连接直接触发 tempdb 大量页分配。
CREATE TABLE #orders (order_id INT, status_id TINYINT, created_time DATETIME2)-
CREATE CLUSTERED INDEX IX_orders_order_id ON #orders (order_id)(哪怕只有一列,也比没有强) -
CREATE NONCLUSTERED INDEX IX_orders_status_time ON #orders (status_id, created_time)(匹配后续 WHERE / JOIN 条件) - 避免在循环里反复
INSERT INTO #orders SELECT ...而不DROP TABLE #orders,版本存储会累积占用空间
大排序操作必须控制数据规模,不能无脑 ORDER BY
ORDER BY、GROUP BY、窗口函数、哈希聚合这些操作,只要数据量超内存阈值,就会把中间结果刷到 tempdb。几十万行一排,tempdb 就开始喘。
- 先过滤再排序:
WHERE created_time >= '2026-05-01'放在ORDER BY前,别让全表进排序流程 - 用
TOP N或分页(OFFSET ... FETCH)限制输出行数,尤其在报表类存储过程中 - 检查执行计划里是否有“警告:缺少统计信息”或“警告:内存授予不足”,这是排序溢出的前兆
- 对
tempdb文件配置多个数据文件(建议数量 = CPU 逻辑核心数),避免单文件 I/O 瓶颈
查谁在吃 tempdb:用 DMV 定位罪魁祸首
别猜,直接查。以下查询能快速揪出当前占空间最多的会话和语句:
SELECT CAST(SUM(su.user_objects_alloc_page_count + su.internal_objects_alloc_page_count) * 8.0 / 1024 AS DECIMAL(10,2)) AS [Allocated_MB], CAST(SUM(su.user_objects_dealloc_page_count + su.internal_objects_dealloc_page_count) * 8.0 / 1024 AS DECIMAL(10,2)) AS [Deallocated_MB], su.session_id, es.login_name, es.host_name, es.program_name, st.text AS [LastQuery] FROM sys.dm_db_session_space_usage su JOIN sys.dm_exec_sessions es ON su.session_id = es.session_id OUTER APPLY sys.dm_exec_sql_text(es.most_recent_sql_handle) st WHERE su.session_id > 50 GROUP BY su.session_id, es.login_name, es.host_name, es.program_name, st.text HAVING SUM(su.user_objects_alloc_page_count + su.internal_objects_alloc_page_count) > 0 ORDER BY [Allocated_MB] DESC;
重点看 [Allocated_MB] 和 [LastQuery] 列。如果某条语句反复出现且数值持续上涨,基本就是它在循环写临时表或没提交事务。
表变量 vs 临时表:别迷信“小就快”
表变量 @t 不是银弹。SQL Server 默认给它估 1 行,一旦实际插入几千行,执行计划就崩——比如走嵌套循环而非哈希连接,反而更耗 tempdb 和 CPU。
- 数据量 @t 没问题
- 数据量 ≥ 500 行,且要 JOIN / WHERE / ORDER BY → 必须用
#t+ 显式索引 - 想彻底绕开
tempdb?考虑内存优化表变量(SCHEMA_ONLY),但需开启 In-Memory OLTP 功能,且不支持所有数据类型
真正难处理的不是单次爆 tempdb,而是那种“每次跑都慢一点、隔几天就挂一次”的渐进式膨胀——往往藏在没显式 DROP 的临时表、没 COMMIT 的长事务、或 CTE 被多次引用却没加 OPTION (RECOMPILE) 的细节里。










