Pau*_*ite 47 sql-server execution-plan memory-grant
为什么这个简单的查询被授予如此多的内存?
-- Demo table
CREATE TABLE dbo.Test
(
TID integer IDENTITY NOT NULL,
FilterMe integer NOT NULL,
SortMe integer NOT NULL,
Unused nvarchar(max) NULL,
CONSTRAINT PK_dbo_Test_TID
PRIMARY KEY CLUSTERED (TID)
);
GO
-- 100,000 example rows
INSERT dbo.Test WITH (TABLOCKX)
(FilterMe, SortMe)
SELECT TOP (100 * 1000)
CHECKSUM(NEWID()) % 1000,
CHECKSUM(NEWID())
FROM sys.all_columns AS AC1
CROSS JOIN sys.all_columns AS AC2;
GO
-- Query
SELECT
T.TID,
T.FilterMe,
T.SortMe,
T.Unused
FROM dbo.Test AS T
WHERE
T.FilterMe = 567
ORDER BY
T.SortMe;
Run Code Online (Sandbox Code Playgroud)
对于估计 50 行,优化器为排序保留了近 500 MB:

Pau*_*ite 42
这是SQL Server 中的一个错误(从 2008 年到 2014 年)。
我的错误报告在这里。
过滤条件作为残差谓词被下推到扫描运算符中,但是根据预过滤器基数估计错误地计算了授予排序的内存。
为了说明这个问题,我们可以使用(未记录和不受支持的)跟踪标志 9130 来防止过滤器被下推到扫描运算符中。授予排序的内存现在正确地基于过滤器输出的估计基数,而不是扫描:
SELECT
T.TID,
T.FilterMe,
T.SortMe,
T.Unused
FROM dbo.Test AS T
WHERE
T.FilterMe = 567
ORDER BY
T.SortMe
OPTION (QUERYTRACEON 9130); -- Not for production systems!
Run Code Online (Sandbox Code Playgroud)

对于生产系统,需要采取措施来避免有问题的计划形状(将过滤器推入扫描并在另一列上进行排序)。一种方法是提供过滤条件的索引和/或提供所需的排序顺序。
-- Index on the filter condition only
CREATE NONCLUSTERED INDEX IX_dbo_Test_FilterMe
ON dbo.Test (FilterMe);
Run Code Online (Sandbox Code Playgroud)
有了这个索引,排序所需的内存授权仅为928KB:

更进一步,以下索引可以完全避免排序(零内存授予):
-- Provides filtering and sort order
-- nvarchar(max) column deliberately not INCLUDEd
CREATE NONCLUSTERED INDEX IX_dbo_Test_FilterMe_SortMe
ON dbo.Test (FilterMe, SortMe);
Run Code Online (Sandbox Code Playgroud)

在以下版本的 SQL Server x64 开发人员版上进行了测试和错误确认:
SELECT
T.TID,
T.FilterMe,
T.SortMe,
T.Unused
FROM dbo.Test AS T
WHERE
T.FilterMe = 567
ORDER BY
T.SortMe
OPTION (QUERYTRACEON 9130); -- Not for production systems!
Run Code Online (Sandbox Code Playgroud)
这已在SQL Server 2016 Service Pack 1 中得到修复。发行说明包括以下内容:
VSTS 错误编号 8024987
带有下推谓词的表扫描和索引扫描往往会高估父运算符的内存授予。
测试并确认固定在:
Microsoft SQL Server 2016 (SP1) - 13.0.4001.0 (X64) Developer EditionMicrosoft SQL Server 2014 (SP2-CU3) 12.0.5538.0 (X64) Developer Edition两种 CE 型号。
从 SQL 2012 开始,您可以查找SerialRequiredMemory和之间的较大差异SerialDesiredMemory,例如:
-- Search plan cache for Memory Grant issues
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp
-- Collect more info about the plan here if required, eg usecounts, objtype etc,
SELECT IDENTITY( INT, 1, 1 ) rowId, query_plan
INTO #tmp
FROM sys.dm_exec_cached_plans cp WITH(NOLOCK)
CROSS APPLY sys.dm_exec_query_plan(plan_handle)
GO
;WITH cte AS
(
SELECT
rowId,
query_plan,
m.c.value ('@SerialRequiredMemory', 'INT' ) AS SerialRequiredMemory,
m.c.value ('@SerialDesiredMemory', 'INT' ) AS SerialDesiredMemory
FROM #tmp t
CROSS APPLY t.query_plan.nodes ( '//*:MemoryGrantInfo[@SerialDesiredMemory[. > 0]]' ) m(c)
), cte2 AS (
SELECT *,
CAST( CAST( SerialDesiredMemory AS DECIMAL(10,2) ) / CAST( SerialRequiredMemory AS DECIMAL(10,2) ) AS DECIMAL(10,2) ) Desired_to_Required_ratio
FROM cte
)
SELECT TOP 20
rowId,
query_plan,
SerialRequiredMemory SerialRequiredMemory_KB,
SerialDesiredMemory SerialDesiredMemory_KB,
CAST( SerialRequiredMemory / 1024. AS DECIMAL(10,2) ) SerialRequiredMemory_MB,
CAST( SerialDesiredMemory / 1024. AS DECIMAL(10,2) ) SerialDesiredMemory_MB,
Desired_to_Required_ratio
FROM cte2
WHERE Desired_to_Required_ratio > 100
ORDER BY Desired_to_Required_ratio DESC
Run Code Online (Sandbox Code Playgroud)
这些新的属性有些还注意到这里。这个查询有点粗略,但确实从我的 SQL Server 2014 开发箱中提取了过多的排序查询,比率为 975.47 加上其他几个令人瞠目结舌的计划。“正常”比率(至少从我有限的测试来看)似乎是 ~1。
HTH
| 归档时间: |
|
| 查看次数: |
11097 次 |
| 最近记录: |