Monitor SQL Server TempDB Size and Alerting Script

By:   |   Updated: 2019-04-18   |   Comments (7)   |   Related: More > Database Administration


Problem

SQL Server database space monitoring has two parts, checking remaining disk space and internal free space. Most monitoring software provides free space monitoring at the disk level only. Furthermore, TempDB has multiple data files, so you need to do a little math to see if the remaining disk space can accommodate the next auto growth.  In this tip we look at a SQL Server stored procedure you can use to send alerts if there are space issues with TempDB.

Solution

I have written a stored procedure to monitor SQL Server TempDB free space and send an alert based on a defined threshold. It is always a good practice to pre-size the drive and growth settings, but having an alert avoids mistakes and downtime in some cases. The complete stored procedure is listed at the end of the article. Simply, update the mail profile name and the error handling section as per your needs and you are good to go.

Explanation of TempDB Monitoring Stored Procedure

I have broken up the stored procedure logic into the below steps for explanation.

Check TempDB Auto Growth Settings

 Check the auto growth settings and alert if it’s set in percentages instead of absolute values.

----alert if auto-grow is set in percentage 
IF EXISTS(SELECT 1 FROM tempdb.sys.database_files WHERE is_percent_growth = 1)
BEGIN
   EXEC msdb.dbo.sp_send_dbmail 
      @profile_name ='no-reply'
     ,@recipients = @recipients
     ,@body ='Please update tempdb auto growth settings. It should be in absolute size as per best practices.'
     ,@subject ='Alert : Tempdb auto grow settings'
     ,@body_format='HTML'

   RETURN;
END

Check TempDB Available Disk Space

Find how much space left on the disk drive and what the required size for auto growth.

Auto growth for TempDB is not as simple as other user databases due to multiple data files of TempDB, especially when you have trace flags 1117 and 1118 enabled or you are on SQL Server 2016 and later.  TempDB needs enough space to auto grow all data files equally and simultaneously on SQL Server 2016 and later or when above the Trace Flags are enabled.

The below T-SQL code calculates the remaining free space on the TempDB disk drive and space required for auto growth of data files and log file.

--- find remaining space on the [tempdb] disk drive 
SELECT @drive_space_rem=CONVERT(INT,dovs.available_bytes/1048576.0)
FROM sys.master_files mf
CROSS APPLY sys.dm_os_volume_stats(mf.database_id,mf.FILE_ID)dovs
WHERE DB_NAME(dovs.database_id)='tempdb'
  AND mf.file_id = 1
 
--- size required for data files autogrowth
SELECT @mdf_growth_req=(max(growth)/128)*count(1)
FROM tempdb.sys.database_files
WHERE type = 0
 
-- size required for log files auto growth        
SELECT @ldf_growth_req=(max(growth)/128)*count(1)
FROM tempdb.sys.database_files
WHERE type = 1

Find Internal Free Space in TempDB

Find total internal free space across the data files and log file.

We will use Perfmon counters within SQL Server to read internal free space values for data files and log file. Free space values are in MB/percentages, so that it can be compared to defined threshold in either size (MB/percentage) later in the procedure. I have noticed this piece is missing in monitoring tools I have used so far, they either allow to alert based on absolute size or percentage but not a combination.

Setting a flat threshold in percentage or size doesn’t always work. Like 2% for a very small and large drive is not the same amount of disk space and you may not want to get woken up in the middle of the night for nothing. This procedure gives you the flexibility to set the threshold in either unit and is very useful while working on servers of different scales or environments.

--- read internal free space for data files and log files
SELECT
   @mdf_internal_free_mb = [Free Space in tempdb (KB)] /1024 ,
   @mdf_internal_free_perc = ((([Free Space in tempdb (KB)] / 1024)*100)/([Data File(s) Size (KB)]/1024)),
   @ldf_internal_free_mb = (([Log File(s) Size (KB)]/1024)-([Log File(s) Used Size (KB)]/1024)),
   @ldf_internal_free_perc = ( 100- [Percent Log Used])
FROM (SELECT counter_name, cntr_value,cntr_type 
      FROM sys.dm_os_performance_counters
      WHERE counter_name IN
        ('Data File(s) Size (KB)',
         'Free Space in tempdb (KB)',
         'Log File(s) Size (KB)',
         'Log File(s) Used Size (KB)',
         'Percent Log Used'
        )
        AND (instance_name = 'tempdb' or counter_name like '%tempdb%')
     ) AS A
PIVOT
     ( MAX(cntr_value)FOR counter_name IN
      ([Data File(s) Size (KB)],
       [Free Space in tempdb (KB)],
       [LOG File(s) Size (KB)],
       [Log File(s) Used Size (KB)],
       [Percent Log Used])
      )AS B

Determine if SQL Server Alerts Should Be Sent

Evaluate if we need to alert the DBA team. If internal free space is under the defined threshold and remaining disk space cannot accommodate the next auto growth, send an alert.

---- check the data files free space against threshold
IF(@mdf_growth_req> @drive_space_rem and (@mdf_internal_free_perc < @mdf_internal_perc_threshold OR @mdf_internal_free_mb< @mdf_internal_mb_threshold))
BEGIN
   SET @mdfsubject= @mdfsubject+'Alert : Tempdb data files short on space '+' On '

   SET @mdftableHTML = N'<h3> '+'Total tempdb remaining space '+'</h3>'+CHAR(13)
   SET @mdftableHTML = @mdftableHTML+N'<table border="2" cellpadding="2" cellspacing="0">'+CHAR(13)
   SET @mdftableHTML = @mdftableHTML+N'<tr><th>free_space_datafiles</th><th>free_space_log_file</th></tr>'+CHAR(13)
   SET @mdftableHTML = @mdftableHTML+CAST((SELECT @mdf_internal_free_mb as TD,
                                                  @ldf_internal_free_mb AS TD 
                                           FOR XML RAW ('tr'),ELEMENTS)as varchar(MAX))+CHAR(13)
   SET @mdftableHTML = @mdftableHTML+N'</table>'+'<br>'+CHAR(13)
   SET @mdfsubject = @mdfsubject+@@SERVERNAME

   -- send an email
   EXEC msdb.dbo.sp_send_dbmail  
       @profile_name='no-reply'
      ,@recipients = @recipients
      ,@body = @mdftableHTML
      ,@subject = @mdfsubject
      ,@body_format='HTML'
END
 
---- check the log file free space against threshold 
IF(@ldf_growth_req > @drive_space_rem and( @ldf_internal_free_perc < @ldf_internal_perc_threshold OR @ldf_internal_free_mb < @ldf_internal_mb_threshold))
BEGIN
   SET @ldfsubject= @ldfsubject+'Alert : Tempdb log file short on space '+' On '

   SET @ldftableHTML = N'<h3> '+'Total tempdb remaining space '+'</h3>'+CHAR(13)
   SET @ldftableHTML = @ldftableHTML+N'<table border="2" cellpadding="2" cellspacing="0">'+CHAR(13)
   SET @ldftableHTML = @ldftableHTML+N'<tr><th>free_space_datafiles</th><th>free_space_log_file</th></tr>'+CHAR(13)
   SET @ldftableHTML = @ldftableHTML+CAST((SELECT @mdf_internal_free_mb as TD,
                                                  @ldf_internal_free_mb AS TD 
                                           FOR XML RAW('tr'),ELEMENTS)as varchar(MAX))+CHAR(13)
   SET @ldftableHTML = @ldftableHTML+N'</table>'+'<br>'+CHAR(13)

   SET @ldfsubject = @ldfsubject+@@SERVERNAME

   -- send an email
   EXEC msdb.dbo.sp_send_dbmail 
       @profile_name='no-reply'
      ,@recipients = @recipients
      ,@body = @ldftableHTML
      ,@subject = @ldfsubject
      ,@body_format='HTML'
END

Complete Stored Procedure to Check TempDB Space Usage and Free Space

Finally put the stored procedure in a SQL Server Agent Job and set the threshold values as per your needs and schedule the job. 

You will need to make some adjustments to the stored procedure to use the Database Mail profile you have set up on your database instance. 

Then just run the procedure and pass in the parameters for recipients and any of the other values if you want to use values other than the defaults I provided.

/****** Object:  StoredProcedure [dbo].[tempdb_space_alerting]   ******/
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE PROC [dbo].[tempdb_space_alerting] 
--------------------------------------------------------------------------------
-- LOCATION: database name 
-- AUTHOR: Aakash Patel
-- DATE: 01/23/2019
-- INPUTS: 
 @recipients VARCHAR(200)
,@mdf_internal_mb_threshold smallint  = 2048
,@mdf_internal_perc_threshold tinyint = 2
,@ldf_internal_mb_threshold smallint  = 1024
,@ldf_internal_perc_threshold tinyint = 3
-- OUTPUTS:
-- DESCRIPTION: Monitors free space for tempdb and alerts if required. 
-- WHICH APP CALLS THIS : Agent Job
-- FREQUENCY: every xx seconds
-- MODIFICATION HISTORY: 
-- 01/23/2019 – Aakash Patel : Initial Build 
----------------------------------------------------------------------------- 
AS
BEGIN
 
BEGIN TRY
DECLARE @mdf_growth_req int
DECLARE @ldf_growth_req int
DECLARE @drive_space_rem int
DECLARE @mdf_internal_free_perc tinyint
DECLARE @mdf_internal_free_mb int
DECLARE @ldf_internal_free_perc tinyint
DECLARE @ldf_internal_free_mb int
DECLARE @mdfsubject varchar(100)=''
DECLARE @ldfsubject varchar(100)=''
DECLARE @mdftableHTML VARCHAR(MAX)
DECLARE @ldftableHTML VARCHAR(MAX)
 
----alert if auto-grow is set in percentage 
IF EXISTS(SELECT 1 FROM tempdb.sys.database_files
Where is_percent_growth=1 )
BEGIN
EXEC msdb.dbo.sp_send_dbmail @profile_name ='no-reply'
,@recipients = @recipients
,@body ='Please update tempdb auto growth settings. It should be in absolute size as per best practices.'
,@subject ='Alert : Tempdb auto grow settings'
,@body_format='HTML'
RETURN;
END
 
--- find remaining space on the [tempdb] disk drive 
SELECT
@drive_space_rem=CONVERT(INT,dovs.available_bytes/1048576.0)
FROM sys.master_files mf
CROSS APPLY sys.dm_os_volume_stats(mf.database_id,mf.FILE_ID)dovs
where DB_NAME(dovs.database_id)='tempdb'
AND mf.file_id= 1
 
--- size required for data files auto growth
select @mdf_growth_req=(max(growth)/128)*count(1)
from tempdb.sys.database_files
where type= 0

-- size required for log file auto growth        
select @ldf_growth_req=(max(growth)/128)*count(1)
from tempdb.sys.database_files
where type= 1
 
--- fetch internal free space for data files and log files
SELECT
   @mdf_internal_free_mb= [Free Space in tempdb (KB)] /1024 ,
   @mdf_internal_free_perc=((([Free Space in tempdb (KB)] / 1024)*100)/([Data File(s) Size (KB)]/1024)),
   @ldf_internal_free_mb=(([Log File(s) Size (KB)]/1024)-([Log File(s) Used Size (KB)]/1024)),
   @ldf_internal_free_perc=( 100- [Percent Log Used])
FROM (SELECT counter_name,cntr_value,cntr_type FROM sys.dm_os_performance_counters
WHERE counter_name IN
('Data File(s) Size (KB)',
'Free Space in tempdb (KB)',
'Log File(s) Size (KB)',
'Log File(s) Used Size (KB)',
'Percent Log Used')
AND(instance_name='tempdb'or counter_name like'%tempdb%'))AS A
PIVOT
(MAX(cntr_value)FOR counter_name IN
([Data File(s) Size (KB)],
[Free Space in tempdb (KB)],
[LOG File(s) Size (KB)],
[Log File(s) Used Size (KB)],
[Percent Log Used]))AS B
 
---- check the data files free space against threshold
IF(@mdf_growth_req> @drive_space_rem and (@mdf_internal_free_perc < @mdf_internal_perc_threshold OR @mdf_internal_free_mb< @mdf_internal_mb_threshold))
BEGIN
   SET @mdfsubject= @mdfsubject+'Alert : Tempdb data files short on space '+' On '
   SET @mdftableHTML=N'<h3> '+'Total tempdb remaining space '+'</h3>'+CHAR(13)
   SET @mdftableHTML=  @mdftableHTML+N'<table border="2" cellpadding="2" cellspacing="0">'+CHAR(13)
   SET @mdftableHTML=  @mdftableHTML+N'<tr><th>free_space_datafiles</th><th>free_space_log_file</th></tr>'+CHAR(13)
   SET @mdftableHTML=  @mdftableHTML+CAST((SELECT @mdf_internal_free_mb as TD,
                                       @ldf_internal_free_mb AS TD 
      FOR XML RAW ('tr'),ELEMENTS)as varchar(MAX))+CHAR(13)
   SET @mdftableHTML=  @mdftableHTML+N'</table>'+'<br>'+CHAR(13)
   SET @mdfsubject= @mdfsubject+@@SERVERNAME
   -- send an email
   EXEC msdb.dbo.sp_send_dbmail @profile_name='no-reply'
   ,@recipients = @recipients
   ,@body = @mdftableHTML
   ,@subject = @mdfsubject
   ,@body_format='HTML'
END
 
---- check the log file free space against threshold
IF(@ldf_growth_req> @drive_space_rem and( @ldf_internal_free_perc< @ldf_internal_perc_threshold OR @ldf_internal_free_mb< @ldf_internal_mb_threshold))
BEGIN
   SET @ldfsubject= @ldfsubject+'Alert : Tempdb log file short on space '+' On '
   SET @ldftableHTML=N'<h3> '+'Total tempdb remaining space '+'</h3>'+CHAR(13)
   SET @ldftableHTML=  @ldftableHTML+N'<table border="2" cellpadding="2" cellspacing="0">'+CHAR(13)
   SET @ldftableHTML=  @ldftableHTML+N'<tr><th>free_space_datafiles</th><th>free_space_log_file</th></tr>'+CHAR(13)
   SET @ldftableHTML=  @ldftableHTML+CAST((SELECT @mdf_internal_free_mb as TD,
                                       @ldf_internal_free_mb AS TD 
      FOR XML RAW('tr'),ELEMENTS)as varchar(MAX))+CHAR(13)
   SET @ldftableHTML=  @ldftableHTML+N'</table>'+'<br>'+CHAR(13)
   SET @ldfsubject= @ldfsubject+@@SERVERNAME
   -- send an email
   EXEC msdb.dbo.sp_send_dbmail @profile_name='no-reply'
   ,@recipients = @recipients
   ,@body = @ldftableHTML
   ,@subject = @ldfsubject
   ,@body_format='HTML'
END
 
END TRY
 
----- error handling 
BEGIN CATCH
   --Insert your standard error handling 
END CATCH
 
END;
 
GO
Next Steps
  • Test this out in one of your test environments to see how this can be used before using in production.
  • Set up a SQL Server Agent job to send you daily updates on TempDB space usage.
  • Check out these other Database Administration Tips


sql server categories

sql server webinars

subscribe to mssqltips

sql server tutorials

sql server white papers

next tip



About the author
MSSQLTips author Aakash Patel Aakash Patel is a Senior SQL Server DBA for a software firm in Connecticut with 10+ years of experience.

This author pledges the content of this article is based on professional experience and not AI generated.

View all my tips


Article Last Updated: 2019-04-18

Comments For This Article




Tuesday, May 11, 2021 - 9:16:34 AM - Aakash Back To Top (88670)
Hi Vijay,

It looks like you are missing some code, please copy the complete stored procedure listed at the end of article and try again.

Thanks,
Aakash

Tuesday, May 11, 2021 - 5:05:28 AM - Vijay Back To Top (88668)
Error

Msg 137, Level 15, State 2, Line 87
Must declare the scalar variable "@mdf_internal_perc_threshold".
Msg 137, Level 15, State 2, Line 100
Must declare the scalar variable "@recipients".
Msg 137, Level 15, State 2, Line 107
Must declare the scalar variable "@ldf_internal_perc_threshold".
Msg 137, Level 15, State 2, Line 120
Must declare the scalar variable "@recipients".
Msg 102, Level 15, State 1, Line 131
Incorrect syntax near 'CATCH'.

Monday, October 26, 2020 - 5:27:38 PM - Greg Robidoux Back To Top (86699)
Hi Rob, this is showing how much free space exists in the data file and the log file. It is showing the values in MB and and as an overall percentage free value.

Monday, October 26, 2020 - 4:40:56 PM - Rob Back To Top (86698)
I really like this! I am considering installing this for monitoring tempdb, since I don't have a monitoring tool. We are such a small shop that our budget won't allow for a monitoring tool.

Can you provide more explanation about what is being selected here?
@mdf_internal_free_mb = [Free Space in tempdb (KB)] /1024 ,
@mdf_internal_free_perc = ((([Free Space in tempdb (KB)] / 1024)*100)/([Data File(s) Size (KB)]/1024)),
@ldf_internal_free_mb = (([Log File(s) Size (KB)]/1024)-([Log File(s) Used Size (KB)]/1024)),
@ldf_internal_free_perc = ( 100- [Percent Log Used])

Friday, July 12, 2019 - 10:17:33 AM - Aakash Patel Back To Top (81733)

Hi Ronald,

The script has been corrected in the article. Thank you for the feedback.


Tuesday, July 2, 2019 - 12:16:00 PM - Aakash Back To Top (81658)

Good Catch Ronald , I will correct the script in the article soon.


Tuesday, July 2, 2019 - 6:52:47 AM - Ronald Back To Top (81656)

Hi,

There is a bug in the code in the line:

---- check the data files free space against threshold 

IF(@mdf_growth_req> @drive_space_rem and (@mdf_internal_free_perc < @mdf_internal_perc_threshold OR  @mdf_internal_free_mb< @mdf_internal_free_mb))

It should be 

 ---- check the data files free space against threshold

IF(@mdf_growth_req> @drive_space_rem and (@mdf_internal_free_perc < @mdf_internal_perc_threshold OR  @mdf_internal_free_mb< @mdf_internal_mb_threshold))















get free sql tips
agree to terms