Be aware of 701 error if you use memory optimized table variable in a loop
In blog “Importance of choosing correct bucket count of hash indexes on a memory optimized table”, I talked about encountering performance issues with incorrect sized bucket count. I was actually investigating an out of memory issues with the following error.
Msg 701, Level 17, State 103, Line 11
There is insufficient system memory in resource pool 'default' to run this query.
I simplified the scenario but customer’s code is very similar to the loop below. Basically, this customer tried to insert 1 million row into a memory optimized table variable and process them. Then he deleted the rows from the memory optimized table variable and inserted another 1 million. His goal was to process 1 billion rows. But before he was able to process 1 billion rows, he would run out of memory (701 error as above)
DECLARE @t2 AS [SalesOrderDetailType_inmem]
insert into @t2 select * from t2
while 1 = 1 --note that this customer didn’t use 1=1. I just simplified to reproduce it
begin
delete @t2
insert into @t2 select * from t2
end
This customer was puzzled because he delete existing rows. At any given time, there should not be more than 1 million rows. SQL Server should not have run out of memory.
This is actually by-design behavior documented in “Memory-Optimized Table Variables”). Here is what is state “Unlike memory-optimized tables, the memory consumed (including deleted rows) by table variables is freed when the table variable goes out of scope)”. With a loop like above, all deleted rows will be kept and consume memory until end of the loop.
Complete Repro
=============
Step 1 Create a disk based table and populate 1 million rows
CREATE table t2(
[OrderQty] [smallint] NOT NULL,
[ProductID] [int] NOT NULL,
[SpecialOfferID] [int] NOT NULL,
[LocalID] [int] NOT NULL
)
Step 2 create a type using memory optimized table
CREATE TYPE [SalesOrderDetailType_inmem] AS TABLE(
[OrderQty] [smallint] NOT NULL,
[ProductID] [int] NOT NULL,
[SpecialOfferID] [int] NOT NULL,
[LocalID] [int] NOT NULL,
INDEX [IX_ProductID] HASH ([ProductID]) WITH ( BUCKET_COUNT = 1000000),
INDEX [IX_SpecialOfferID] NONCLUSTERED (LocalID)
)
WITH ( MEMORY_OPTIMIZED = ON )
Step 3 run the following query and eventually you will run of memory
DECLARE @t2 AS [SalesOrderDetailType_inmem]
insert into @t2 select * from t2
while 1 = 1
begin
delete @t2
insert into @t2 select * from t2
end
Jack Li |Senior Escalation Engineer | Microsoft SQL Server
Comments
- Anonymous
February 23, 2017
I like this post, enjoyed this one regards for posting. - Anonymous
February 24, 2017
Okay, so if you have to loop, how would you work around that? Dynamic SQL for the whole thing? - Anonymous
March 16, 2017
The comment has been removed - Anonymous
April 18, 2017
Hi Jack. It would be good to know what solution options exist for this scenario. Thanks- Anonymous
May 08, 2017
Alex, the solution would be to use a memory-optimized table, not a memory-optimized table variable. Please refer to Scenario B or Scenario C in this post: http://bit.ly/2pcuKU3- Anonymous
December 13, 2017
Thank you, this is what I just looked for. We have just installed SQL 2016, immediately I thought I test all cool feature. I have cursor which uses #temp table and works perfectly, I have spent time lot of time to optimize it.I saw couple nice test about the memory table variable and it is faster really faster than the temp database. I thought I just what I need.I rewrote the code and after I managed to run it, the code worked perfectly I thought I run on the server on the whole night and I have the required data by morning.The first thing when I got up of course to check the result and what I got the insufficient memory error message.I figured out the issue is the same.I use memory optimized table variable and I inserted 16000 records into 23 column (varchar(15) wide memory optimized table and 23 times deleted and inserted the remaining selected records to find the best only one records I need. I run it 60000 times and using 12 instances.the server has 1 TB memory. Some of the process finished the work, some of them failed and probably I used all resources of the server.Lesson learned.
- Anonymous
- Anonymous