Takeaway: The best way to avoid tempdb GAM and PFS contention caused by table-valued parameters (TVPs) is to use Memory-Optimized Table Variables.
This is my last post about tempdb. You can’t believe how wonderful it is to type that.*
SQL Server 2014 can eradicate tempdb contention and I didn’t even know it. Just yesterday I was saying that if I could have my one SQL Server wish, it would be to use table-valued parameters at high frequency without suffering from tempdb latch contention on GAM or PFS.
Then, I saw Jos de Bruijn’s article Improving temp table and table variable performance using memory optimization. He pointed out how In-Memory OLTP can alleviate excessive tempdb use. The piece that I was most interested in involves SQL Server 2014’s Memory-Optimized Table Parameters. Jos describes them as a great solution for “workloads that intensively use TVPs”.
So now in addition to database with types like this:
CREATE TYPE dbo.BIGINTSET AS TABLE ([Value] BIGINT NOT NULL INDEX IX_BIGINTSET);
I can add an additional memory optimized type like this:
CREATE TYPE dbo.BIGINTSET_OLTP AS TABLE ([Value] BIGINT NOT NULL INDEX IX_BIGINTSET_OLTP) WITH (MEMORY_OPTIMIZED=ON);
Any procedures that use the new table type
BIGINTSET_OLTP will not touch tempdb.
It seems pretty easy doesn’t it? In this example, the reason I don’t just replace the existing type is because I want to be very deliberate about choosing memory optimized parameters. By adding a new table type, I make that choice in each procedure or query on a case by case basis.
To see whether this technique avoids hitting tempdb as advertised, I wrote this demo.
A Complete Demo
use master GO IF ( DB_ID('MyTVPTest') IS NOT NULL ) BEGIN ALTER DATABASE MyTVPTest SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE MyTVPTest; END GO CREATE DATABASE MyTVPTest; ALTER DATABASE MyTVPTest ADD FILEGROUP MyTVPTest_MOD CONTAINS MEMORY_OPTIMIZED_DATA; ALTER DATABASE MyTVPTest ADD FILE (name='MyTVPTest_MOD1', filename='c:\sqldata\MyTVPTest_MOD1') TO FILEGROUP MyTVPTest_MOD; GO use MyTVPTest; GO CREATE TYPE dbo.MyMOIntSet AS TABLE ([Value] INT NOT NULL INDEX IX_MyMOIntSet) WITH (MEMORY_OPTIMIZED=ON); CREATE TYPE dbo.MyIntSet AS TABLE ([Value] INT NOT NULL INDEX IX_MyIntSet); GO CREATE TABLE dbo.MyTable ( id int not null primary key ); INSERT dbo.MyTable (id) SELECT top 1000 ROW_NUMBER() OVER (ORDER BY (SELECT 1)) FROM sys.messages; GO CREATE PROCEDURE dbo.s_FetchMyTableRowsWithFilter @Filter MyIntSet READONLY AS SELECT MT.id FROM dbo.MyTable MT JOIN @Filter FT ON FT.[Value] = MT.id; GO CREATE PROCEDURE dbo.s_FetchMyTableRowsWithMOFilter @Filter MyMOIntSet READONLY AS SELECT MT.id FROM dbo.MyTable MT JOIN @Filter FT ON FT.[Value] = MT.id; GO -- Call procedure using Memory Optimized table type DECLARE @F_MO MyMOIntSet; INSERT @F_MO ([Value]) VALUES (1), (3), (5); EXEC dbo.s_FetchMyTableRowsWithMOFilter @F_MO; -- Call procedure using regular table type DECLARE @F MyIntSet; INSERT @F ([Value]) VALUES (1), (3), (5); EXEC dbo.s_FetchMyTableRowsWithFilter @F;
Look at the last two queries. The first one invokes a procedure that uses a memory-optimized table variable. The other one invokes a different procedure that uses a standard table variable. Then look at what’s going on in tempdb’s transaction log (using techniques found here). With the standard table variable I see this sort of activity in tempdb’s log:
But I see that memory-optimized queries avoids all tempdb activity. It’s not just the logging activity that’s avoided, all tempdb activity is avoided. The tempdb is simply not touched in this case.
Notice a few things:
- This mainly addresses tempdb contention experienced by workloads with extremely frequent TVP use (thousands per second).
- In SQL Server 2014, memory-optimized table variables precludes the use of parallel queries.
- As the feature name suggests, the temp tables live in memory.
Luckily the high frequency described by the first caveat means that the queries I use must be as lightweight as possible. And that means that I don’t mind the second or third caveat so much. Aaron Bertrand dives deeper on the performance of Memory-Optimized table variables at Hekaton with a twist: In-memory TVPs – Part 3.
In the past, my colleagues and I have been incredibly frustrated at this bottleneck. A bottleneck that could not be fixed, not even with dollars. And so I’m looking forward to this feature more than any other single feature introduced in 2014 or 2016. We vendors are often constrained to use only those features present in every SQL Server version we support. In my case, it will be a while before we de-support SQL Server 2012 but that day will be like Christmas.
* I’ve written before about TVPs and the different kinds of contention caused by frequent use of TVPs: