There is a myth that modifications to tempdb do not take effect until you restart the instance, understandably people have read the message that will be printed during many changes. But with careful planning the message and the restarts can be avoided and many changes can instead be made to take effect immediately.
Recently I came across a server where database integrity checks were causing application disconnects in combination with sudden and massive spikes in latency on one disk. After looking at the configuration I could see 16 cores, MAXDOP 0, and 12x tempdb data files configured on the same spindle.
My interpretation was that MAXDOP would normally be configured in this scenario as 4, and that by over-parallelising and hitting so many tempdb data files the random IO was killing the server.
While it would have been possible to force a query hint to prevent this, I also take the stance that when a server is running on an out-of-the-box defaults configuration (or knee jerk 12x tempdb data file configuration) and is experiencing issues, we should align it to best practices first before digging deeper.
But it was a production server and while we could make changes in the maintenance window we couldn't afford to take it completely offline, and usually when modifying tempdb files you'll be notified that the changes have been written to the system catalog and will take effect on the next restart.
After some experimentation I've documented areas where it is possible to do some changes without a restart - including adding and removing files. Here's a demo configuration out of the box:
Use tempdb
Select name,
type_desc,
physical_name
From sys.database_files df
It's possible to add and remove a data file without a restart… on an inactive system.
Alter Database tempdb
Add File (Name = 'tempdev_2', Filename = 'C:\Program Files\Microsoft SQL Server\MSSQL12.SQL2014\MSSQL\DATA\tempdb_2.ndf')
Alter Database tempdb
Remove File [tempdev_2]
The file 'tempdev_2' has been removed.
But on a system which is in use things are not so easy. I'll demonstrate with two sessions.
-- Session 1
; With Cte As (
Select 1 As Something
Union All
Select Something + 1
From Cte
Where Something < 1000000
)
Select *
Into #OccupyTempdb
From Cte
Option (Maxrecursion 0)
-- Session 2 (while Session 1 is running)
Alter Database tempdb
Add File (Name = 'tempdev_2', Filename = 'C:\Program Files\Microsoft SQL Server\MSSQL12.SQL2014\MSSQL\DATA\tempdb_2.ndf')
Waitfor Delay '00:00:05'
Alter Database tempdb
Remove File [tempdev_2]
Msg 5042, Level 16, State 1, Line 19 The file 'tempdev_2' cannot be removed because it is not empty.
Even after you drop #OccupyTempdb from Session 1 you still cannot remove the file you've just added. You need to shrink and empty the file first.
Dbcc Shrinkfile('tempdev_2', Emptyfile)
Alter Database tempdb
Remove File [tempdev_2]
(1 row(s) affected) DBCC execution completed. If DBCC printed error messages, contact your system administrator. The file 'tempdev_2' has been removed.
It's possible to get an idea of how full the files are with this:
Select size / 128.0 As SizeInMB,
Fileproperty(name, 'SpaceUsed') / 128.0 As UsedInMB,
Cast((Fileproperty(name, 'SpaceUsed') * 100.0) / size As Int) As PercentFull,
name
From sys.database_files sd
It is possible though that if the system is busy enough, you won't be able to shrink and empty the file. This is trickier to reproduce but I was able to flub it (your results may vary).
-- Session 2
Create Database Test_Snapshot
Go
Use Test_Snapshot
Go
; With Cte As (
Select 1 As Something
Union All
Select Something + 1
From Cte
Where Something < 1000000
)
Select *
Into Occupy_Tempdb
From Cte
Option (Maxrecursion 0)
Go
While 1 = 1
Begin
Insert Occupy_Tempdb
Select Something
From Occupy_Tempdb
Order By Newid()
End
-- Session 3
Use Test_Snapshot
Go
While 1 = 1
Begin
Insert Occupy_Tempdb
Select Something
From Occupy_Tempdb
Order By Newid()
End
-- Session 1
Alter Database tempdb
Add File (Name = 'tempdev_2', Filename = 'C:\Program Files\Microsoft SQL Server\MSSQL12.SQL2014\MSSQL\DATA\tempdb_2.ndf')
Waitfor Delay '00:00:05'
Dbcc Shrinkfile('tempdev_2', Emptyfile)
Alter Database tempdb
Remove File [tempdev_2]
DBCC SHRINKFILE: Page 3:127 could not be moved because it is a work table page. DbId FileId CurrentSize MinimumSize UsedPages EstimatedPages ------ ----------- ----------- ----------- ----------- -------------- 2 3 128 128 120 120 (1 row(s) affected) DBCC execution completed. If DBCC printed error messages, contact your system administrator. Msg 5042, Level 16, State 1, Line 43 The file 'tempdev_2' cannot be removed because it is not empty.
Note that that error can't be caught in a Try Catch block.
The main problem with this is that it's not reliable. I have seen the Emptyfile fail and then the Remove File succeed and notify that it has removed the file from the system catalog; this is the worst case scenario because it still shows the file properly in sys.database_files and it's still in use but it can't be shrunk, emptied, or properly removed until you restart the instance.
Unfortunately I was unable to reproduce it for this post. I did find that the @Error variable does get set when Emptyfile fails though, so you should be safe to wrap your Remove File in a quick test to make sure you have the best chance of removing the file without a restart:
Dbcc Shrinkfile('tempdev_2', Emptyfile)
If @@Error = 0
Begin
Alter Database tempdb Remove File [tempdev_2]
End