为什么这个简单的查询被授予如此多的内存?
-- 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;
估计50行,优化器保留近500 MB的排序:
解决方法
这是sql Server中的一个错误(从2008年到2014年).
过滤条件作为残差谓词被下推到扫描操作符中,但是基于预过滤器基数估计错误地计算为该排序授予的存储器.
为了说明问题,我们可以使用(未记录和不支持的)跟踪标志9130来防止过滤器被压入扫描操作符.授予排序的内存现在正确地基于Filter输出的估计基数,而不是扫描:
SELECT T.TID,T.Unused FROM dbo.Test AS T WHERE T.FilterMe = 567 ORDER BY T.sortMe OPTION (QUERYTRACEON 9130); -- Not for production systems!
对于生产系统,需要采取措施以避免有问题的计划形状(将过滤器推入扫描并在另一列上进行排序).一种方法是提供过滤条件的索引和/或提供所需的排序顺序.
-- Index on the filter condition only CREATE NONCLUSTERED INDEX IX_dbo_Test_FilterMe ON dbo.Test (FilterMe);
使用此索引后,排序所需的内存授权仅为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);
在以下sql Server x64 Developer Edition版本上进行了测试和错误确认:
2014 : 12.00.2430 (RTM CU4) 2012 : 11.00.5556 (SP2 CU3) 2008R2 : 10.50.6000 (SP3) 2008 : 10.00.6000 (SP4)
这在SQL Server 2016 Service Pack 1修复.发行说明包括以下内容:
VSTS bug number 8024987
Table scans and index scans with push down predicate tend to overestimate memory grant for the parent operator.
经测试并确认固定于:
> Microsoft sql Server 2016(SP1) – 13.0.4001.0(X64)开发人员版
> Microsoft sql Server 2014(SP2-CU3)12.0.5538.0(X64)开发人员版
两种CE型号.