Home
%3CLINGO-SUB%20id%3D%22lingo-sub-1020997%22%20slang%3D%22en-US%22%3EMemory%20Grant%20Feedback%20-%20SQL%202019%20new%20feature%20applied%20to%20Azure%20SQL%20DB%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1020997%22%20slang%3D%22en-US%22%3E%3CP%3ESQL%20Server%202019%20came%20with%20a%20lot%20of%20performance%20improvements%20that%20also%20applies%20to%20Azure%20SQL%20DB.%20Find%20below%20some%20tests%20I%20made%20on%20my%20own%20instance%20at%20this%20moment%20using%20(%3CSTRONG%3ES3%20tier%3C%2FSTRONG%3E)%20to%20test%26nbsp%3B%3CSTRONG%3EMemory%20Grant%20Feedback%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EFor%20this%20test%20will%20use%20table%20TEST%20(289.536%20rows%20%2F%2043.144%20KB)%20and%20for%20query%20will%20use%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3ESELECT%20object_id%2C%20name%20%0AFROM%20test%0AGROUP%20BY%20object_id%2C%20name%0AHAVING%20COUNT_BIG(1)%20%26gt%3B%201%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EWhen%20a%20query%20like%20this%20starts%20it%20will%20need%20some%20memory%20to%20do%20the%20JOINs%20%2F%20SORTs%20%2F%20etc%20and%20will%20request%20a%20%3CSTRONG%3Ememory%20grant%3C%2FSTRONG%3E.%26nbsp%3BYou%20can%20understand%20more%20about%20memory%20grants%20at%26nbsp%3B%3CA%20title%3D%22Understanding%20SQL%20server%20memory%20grant%22%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2FSQL-Server%2FUnderstanding-SQL-server-memory-grant%2Fba-p%2F383595%22%20target%3D%22_blank%22%20rel%3D%22noopener%22%3EUnderstanding%20SQL%20server%20memory%20grant%3C%2FA%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22padding-left%3A%2030px%3B%22%3E%22%3CFONT%20size%3D%223%22%3E%3CEM%3EFor%20example%2C%20let's%20consider%20a%20simple%20query%20which%20needs%20to%20sort%201%20million%20rows%20of%2010%20bytes%20each%20in%20size.%20The%20required%20memory%20for%20this%20query%20is%20512KB%20because%20this%20is%20the%20minimum%20amount%20SQL%20server%20needs%20to%20construct%20internal%20data%20structures%20to%20handle%20one%20sort.%20Since%20it%20would%20take%2010MB%20to%20store%20all%20rows%2C%20the%20additional%20memory%20would%20be%2010MB%20(slightly%20higher%20when%20overhead%20is%20included).%20This%20calculation%20becomes%20complicated%20if%20the%20compiled%20plan%20has%20multiple%20sorts%20and%20joins%20because%20SQL%20server%20also%20considers%20the%20lifetime%20of%20each%20operator%20for%20more%20efficient%20memory%20usage.%20You%20would%20generally%20see%20smaller%20estimate%20than%20the%20sum%20of%20all%20sorts%20and%20joins%3C%2FEM%3E%3C%2FFONT%3E%22%3C%2FP%3E%0A%3CP%20style%3D%22padding-left%3A%2030px%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EBut%20what%20I%20want%20you%20to%20know%20is%20that%20SQL%20will%20just%20need%20to%20%3CSTRONG%3Eestimate%3C%2FSTRONG%3E%20how%20much%20memory%20will%20be%20needed%20and%20sometimes%20it%20may%26nbsp%3B%3CSTRONG%3Erequest%20more%20than%20needed%3C%2FSTRONG%3E%20or%20it%20might%20also%20request%20%3CSTRONG%3Eless%20memory%20than%20needed.%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EYou%20can%20check%20the%20%3CSTRONG%3Ecurrent%20memory%20grants%3C%2FSTRONG%3E%20for%20the%20database%20using%20query%20below%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3ESELECT%20*%20FROM%20sys.dm_exec_query_memory_grants%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EFind%20below%20some%20tests%20results%20on%20my%20server%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-1054892378%22%20id%3D%22toc-hId-1054892378%22%3ETest%201%20%3A%20Compatibility%20level%20140%20(SQL%202017)%3C%2FH3%3E%0A%3CP%3EBased%20on%20the%20query%20plan%20and%20the%20amount%20of%20memory%20you%20have%2C%20SQL%20will%20estimate%20how%20much%20memory%20you%20may%20need%20in%20this%20case%20%3CSTRONG%3E9.360%20Kb%3C%2FSTRONG%3E%20but%20you%20can%20see%20that%20was%20used%20only%20%3CSTRONG%3E2.608%20Kb%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20style%3D%22width%3A%20999px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Fgxcuf89792.i.lithium.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F158114i7F2FD22F05F05CA8%2Fimage-size%2Flarge%3Fv%3D1.0%26amp%3Bpx%3D999%22%20alt%3D%22clipboard_image_2.png%22%20title%3D%22clipboard_image_2.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId--752562085%22%20id%3D%22toc-hId--752562085%22%3ETest%202%20%3A%20Compatibility%20level%20140%20(SQL%202017)%20%2B%20QUERY%20HINT%20MAX_GRANT_PERCENT%3C%2FH3%3E%0A%3CP%3ETo%20fix%20this%20query%2C%20one%20workaround%20is%20to%20use%20%3CSTRONG%3EResource%20Governor%3C%2FSTRONG%3E%26nbsp%3Bwhere%20we%20could%20control%20the%20memory%20grant%20for%20a%20specific%20workload.%20%3CSTRONG%3EHowever%20its%20is%20not%20available%20for%20Azure%20SQL%20DB.%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EAnother%20solution%20that%20requires%20code%20change%20is%20to%20use%20%3CSTRONG%3Equery%20hints%20(MIN_GRANT_PERCENT%20%2F%20MAX_GRANT_PERCENT)%3C%2FSTRONG%3E%2C%20in%20this%20scenario%20to%20%3CSTRONG%3E0%2C25%25%20of%20the%20available%20memory.%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%3CFONT%20size%3D%222%22%3EMore%20info%20about%20the%20hints%20at%26nbsp%3B%3C%2FFONT%3E%3CA%20href%3D%22https%3A%2F%2Fsupport.microsoft.com%2Fen-us%2Fhelp%2F3107401%2Fnew-query-memory-grant-options-are-available-min-grant-percent-and-max%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CFONT%20size%3D%222%22%3Ehttps%3A%2F%2Fsupport.microsoft.com%2Fen-us%2Fhelp%2F3107401%2Fnew-query-memory-grant-options-are-available-min-grant-percent-and-max%3C%2FFONT%3E%3C%2FA%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3ESELECT%20object_id%2C%20name%20%0AFROM%20test%0AGROUP%20BY%20object_id%2C%20name%0AHAVING%20COUNT_BIG(1)%20%26gt%3B%201%0AOPTION(MIN_GRANT_PERCENT%20%3D%200.25%2C%20MAX_GRANT_PERCENT%20%3D%200.25)%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EIn%20this%20case%20we%20can%20see%20that%20SQL%20wanted%20%3CSTRONG%3E9Mb%3C%2FSTRONG%3E%20but%20only%20granted%20%3CSTRONG%3E4%2C5Mb%3C%2FSTRONG%3E%20and%20still%20using%20only%20%3CSTRONG%3E2Mb%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20style%3D%22width%3A%20400px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Fgxcuf89792.i.lithium.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F158122iC68322EE7FA298FF%2Fimage-size%2Fmedium%3Fv%3D1.0%26amp%3Bpx%3D400%22%20alt%3D%22clipboard_image_3.png%22%20title%3D%22clipboard_image_3.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-1734950748%22%20id%3D%22toc-hId-1734950748%22%3ETest%203%20%3A%20Compatibility%20level%20150%20(SQL%202019)%3C%2FH3%3E%0A%3CP%3ENow%20I%20just%20changed%20the%20database%20compatibility%20using%20SSMS%20wizard%20or%20can%20also%20change%20with%20command%20below.%20There%20is%20no%20need%20to%20restart%20the%20server%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3EALTER%20DATABASE%20%5Bsandbox%5D%20SET%20COMPATIBILITY_LEVEL%20%3D%20150%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3ENow%20using%20new%20compat%20level%20150%20for%20%3CSTRONG%3Efirst%20execution%3C%2FSTRONG%3E%20it%20looks%20like%20the%20initial%20grant%20actually%20have%20increased%2C%20for%20this%20specific%20query%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20style%3D%22width%3A%20400px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Fgxcuf89792.i.lithium.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F158119i962539531152FE44%2Fimage-size%2Fmedium%3Fv%3D1.0%26amp%3Bpx%3D400%22%20alt%3D%22clipboard_image_0.png%22%20title%3D%22clipboard_image_0.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSTRONG%3EBut%20on%20second%20execution%20it%20already%20noticed%20the%20amount%20was%20too%20high%20and%20started%20adjusting%20memory%20grant%20dynamically%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20style%3D%22width%3A%20400px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Fgxcuf89792.i.lithium.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F158120i77EF1BD95EFD5788%2Fimage-size%2Fmedium%3Fv%3D1.0%26amp%3Bpx%3D400%22%20alt%3D%22clipboard_image_1.png%22%20title%3D%22clipboard_image_1.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EAfter%20some%20executions%20SQL%20believe%20that%20he%20have%20a%20good%20amount%20of%20memory%20allocated%20for%20this%20plan%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20style%3D%22width%3A%20400px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Fgxcuf89792.i.lithium.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F158121iFD209BFDFB0A43C1%2Fimage-size%2Fmedium%3Fv%3D1.0%26amp%3Bpx%3D400%22%20alt%3D%22clipboard_image_2.png%22%20title%3D%22clipboard_image_2.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId--72503715%22%20id%3D%22toc-hId--72503715%22%3E%3CSTRONG%3EConclusion%3C%2FSTRONG%3E%3C%2FH3%3E%0A%3CP%3EIn%20a%20scenario%20%2F%20workload%20where%20a%20%3CSTRONG%3Equeries%20reuses%20plans%3C%2FSTRONG%3E%20with%20some%20time%20the%20usage%20of%20memory%20will%20reduce%20and%20you%20will%20have%20better%20overall%20performance.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EYou%20can%20find%20more%20information%20on%20Memory%20Grant%20Feedback%20on%20oficial%20docs%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3E%3CA%20title%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsql%2Frelational-databases%2Fperformance%2Fintelligent-query-processing%3Fview%3Dsql-server-2017%23row-mode-memory-grant-feedback%22%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsql%2Frelational-databases%2Fperformance%2Fintelligent-query-processing%3Fview%3Dsql-server-2017%23row-mode-memory-grant-feedback%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsql%2Frelational-databases%2Fperformance%2Fintelligent-query-processing%3Fview%3Dsql-server-2017%23row-mode-memory-grant-feedback%3C%2FA%3E%3C%2FLI%3E%0A%3CLI%3E%3CA%20title%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2FAzure-SQL-Database%2FIntelligent-Query-Processing-Q-amp-A%2Fba-p%2F446657%22%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2FAzure-SQL-Database%2FIntelligent-Query-Processing-Q-amp-A%2Fba-p%2F446657%22%20target%3D%22_blank%22%20rel%3D%22noopener%22%3Ehttps%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2FAzure-SQL-Database%2FIntelligent-Query-Processing-Q-amp-A%2Fba-p%2F446657%3C%2FA%3E%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-1020997%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EAzure%20SQL%20DB%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E
Microsoft

SQL Server 2019 came with a lot of performance improvements that also applies to Azure SQL DB. Find below some tests I made on my own instance at this moment using (S3 tier) to test Memory Grant Feedback

 

For this test will use table TEST (289.536 rows / 43.144 KB) and for query will use

 

 

SELECT object_id, name 
FROM test
GROUP BY object_id, name
HAVING COUNT_BIG(1) > 1

 

 

When a query like this starts it will need some memory to do the JOINs / SORTs / etc and will request a memory grant. You can understand more about memory grants at Understanding SQL server memory grant 

"For example, let's consider a simple query which needs to sort 1 million rows of 10 bytes each in size. The required memory for this query is 512KB because this is the minimum amount SQL server needs to construct internal data structures to handle one sort. Since it would take 10MB to store all rows, the additional memory would be 10MB (slightly higher when overhead is included). This calculation becomes complicated if the compiled plan has multiple sorts and joins because SQL server also considers the lifetime of each operator for more efficient memory usage. You would generally see smaller estimate than the sum of all sorts and joins"

 

But what I want you to know is that SQL will just need to estimate how much memory will be needed and sometimes it may request more than needed or it might also request less memory than needed.

 

You can check the current memory grants for the database using query below

 

 

SELECT * FROM sys.dm_exec_query_memory_grants

 

 

Find below some tests results on my server

 

Test 1 : Compatibility level 140 (SQL 2017)

Based on the query plan and the amount of memory you have, SQL will estimate how much memory you may need in this case 9.360 Kb but you can see that was used only 2.608 Kb

 

clipboard_image_2.png

 

Test 2 : Compatibility level 140 (SQL 2017) + QUERY HINT MAX_GRANT_PERCENT

To fix this query, one workaround is to use Resource Governor where we could control the memory grant for a specific workload. However its is not available for Azure SQL DB.

 

Another solution that requires code change is to use query hints (MIN_GRANT_PERCENT / MAX_GRANT_PERCENT), in this scenario to 0,25% of the available memory.

More info about the hints at https://support.microsoft.com/en-us/help/3107401/new-query-memory-grant-options-are-available-min-gr...

 

 

 

SELECT object_id, name 
FROM test
GROUP BY object_id, name
HAVING COUNT_BIG(1) > 1
OPTION(MIN_GRANT_PERCENT = 0.25, MAX_GRANT_PERCENT = 0.25)

 

 

In this case we can see that SQL wanted 9Mb but only granted 4,5Mb and still using only 2Mb

 

clipboard_image_3.png

 

Test 3 : Compatibility level 150 (SQL 2019)

Now I just changed the database compatibility using SSMS wizard or can also change with command below. There is no need to restart the server

 

 

ALTER DATABASE [sandbox] SET COMPATIBILITY_LEVEL = 150

 

 

 

Now using new compat level 150 for first execution it looks like the initial grant actually have increased, for this specific query

clipboard_image_0.png

 

But on second execution it already noticed the amount was too high and started adjusting memory grant dynamically

 

clipboard_image_1.png

 

After some executions SQL believe that he have a good amount of memory allocated for this plan

clipboard_image_2.png

 

Conclusion

In a scenario / workload where a queries reuses plans with some time the usage of memory will reduce and you will have better overall performance.

 

You can find more information on Memory Grant Feedback on oficial docs