微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

sql-server – 过多的排序内存授予

为什么这个简单的查询被授予如此多的内存?
-- 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年).

我的错误报告是here.

过滤条件作为残差谓词被下推到扫描操作符中,但是基于预过滤器基数估计错误地计算为该排序授予的存储器.

为了说明问题,我们可以使用(未​​记录和不支持的)跟踪标志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型号.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐