Step 1: How full is tempdb, and what kind of space
USE tempdb;
SELECT SUM(unallocated_extent_page_count) * 8 / 1024 AS free_mb,
SUM(user_object_reserved_page_count) * 8 / 1024 AS user_objects_mb,
SUM(internal_object_reserved_page_count) * 8 / 1024 AS internal_objects_mb,
SUM(version_store_reserved_page_count) * 8 / 1024 AS version_store_mb
FROM sys.dm_db_file_space_usage;
- User objects: temp tables and table variables.
- Internal objects: sorts, hashes and spools from queries, often a query with a bad plan.
- Version store: row versions for snapshot isolation, RCSI, online index rebuilds, triggers and readable secondaries.
Step 2: Which sessions are using it
SELECT su.session_id, s.login_name, s.host_name, s.program_name,
(su.user_objects_alloc_page_count - su.user_objects_dealloc_page_count) * 8 / 1024 AS user_mb,
(su.internal_objects_alloc_page_count - su.internal_objects_dealloc_page_count) * 8 / 1024 AS internal_mb
FROM sys.dm_db_session_space_usage su
JOIN sys.dm_exec_sessions s ON su.session_id = s.session_id
WHERE s.is_user_process = 1
ORDER BY (su.user_objects_alloc_page_count - su.user_objects_dealloc_page_count)
+ (su.internal_objects_alloc_page_count - su.internal_objects_dealloc_page_count) DESC;
-- Space used by queries that are still running
SELECT session_id,
SUM(internal_objects_alloc_page_count - internal_objects_dealloc_page_count) * 8 / 1024 AS running_internal_mb
FROM sys.dm_db_task_space_usage
GROUP BY session_id
ORDER BY running_internal_mb DESC;
Step 3: If the version store is large
The version store can only be cleaned up behind the oldest transaction that still needs it. Find the oldest snapshot transactions:
SELECT TOP (5) at.session_id, at.elapsed_time_seconds, s.login_name, s.program_name
FROM sys.dm_tran_active_snapshot_database_transactions at
JOIN sys.dm_exec_sessions s ON at.session_id = s.session_id
ORDER BY at.elapsed_time_seconds DESC;
Contention: PAGELATCH waits on tempdb
SELECT session_id, wait_type, wait_duration_ms, resource_description
FROM sys.dm_os_waiting_tasks
WHERE wait_type LIKE N'PAGELATCH%'
AND resource_description LIKE N'2:%';
resource_description is database:file:page, and tempdb is database 2. Pages 1, 2 and 3 (PFS, GAM and SGAM, which repeat at intervals in larger files) point to allocation contention. The fix is several equally sized tempdb data files, one per core up to eight, all with the same growth setting. From SQL Server 2019, memory-optimised tempdb metadata helps with contention on system tables.
Never, without approval
- Restarting SQL Server to clear tempdb. It works, but it is an outage for every database on the instance.
- Shrinking tempdb while it is busy. It often fails, and tempdb returns to its configured size at the next restart anyway.
From my SQL Server DBA handbook.