SQL Server 2017:批处理模式内存授予反馈如何工作?

Eri*_*ing 3 sql-server sql-server-2017 memory-grant-feedback

批处理模式内存授予反馈是2017 查询处理器中一系列功能的一部分,其中包括:

那么批处理模式内存授予反馈是如何工作的呢?

Eri*_*ing 5

什么是内存授予反馈?

批处理模式内存授予反馈尝试为查询调整适当大小的内存授予,纠正高估和低估。

当运行需要内存授权的查询时,优化器将请求一个它认为将所有行操作保留在内存中的大小的授权。

通常需要内存授权的事情:

  • 排序
  • 哈希连接

当请求和授予过多内存时,并发性可能会受到影响。内存是一种有限资源,并不是所有东西都能一直使用它。没有无限制访问此类资源之类的东西。

当请求的内存太少时,查询可能会溢出到磁盘。看,没有人希望他们的查询随处可见。磁盘是可怕的。

魔法是如何运作的?

当需要内存授予的计划执行并被缓存时,将重新计算运行该计划所需的实际内存,并相应地更新计划信息。现在,它需要一个 ColumnStore 索引来实现批处理执行模式。

如果魔法不那么神奇呢?

此功能确实有一个终止点,它将回退到原始内存授予。如果运行需要不断重新计算的查询,我们的神奇功能将放弃。最终。在撰写本文时,我还没有关于何时退出的所有实现细节。

我怎么知道它是否有效?

您可以使用扩展事件:

坚果

您还可以在实际执行计划中多次运行的常规查询调整期间观察到它。

你能给我举个例子吗?

当然!这是一个存储过程。在正确的情况下,它会要求不正确的内存授予。

CREATE OR ALTER PROCEDURE dbo.LargeUnusedGrant (@OwnerUserId INT)
AS 
BEGIN

    SELECT TOP 200 *
    FROM dbo.Posts_cx AS p
    WHERE p.OwnerUserId = @OwnerUserId
    AND p.PostTypeId = 1
    ORDER BY p.Score DESC, p.Id DESC;

END;
GO 
Run Code Online (Sandbox Code Playgroud)

查询计划在选择运算符上有警告。

坚果

真是悲哀!我们的查询要求太多内存。

坚果

在第二次执行时,警告将消失。

我听说过一些狡猾的把戏……

Itzik Ben-GanNiko Neugebauer都为需要批处理模式执行的功能和运算符提出了解决方法。

  1. 使用WHERE包含 0 行的子句创建过滤的、非聚集的 ColumnStore 索引
  2. 使用多余的左连接将您的表连接到带有聚集列存储索引的临时表,没有行

这两种方法都是有效的解决方法,可以让它发挥作用!

创建一个空的非聚集 ColumnStore 索引:

/*Itzik's Trizik*/
CREATE NONCLUSTERED COLUMNSTORE INDEX ncci_helper
    ON dbo.Posts
(
    Id,
    AcceptedAnswerId,
    AnswerCount,
    ClosedDate,
    CommentCount,
    CommunityOwnedDate,
    CreationDate,
    FavoriteCount,
    LastActivityDate,
    LastEditDate,
    LastEditorDisplayName,
    LastEditorUserId,
    OwnerUserId,
    ParentId,
    PostTypeId,
    Score,
    ViewCount,
    IsHot )
    WHERE ( Id = -2147483647 AND Id = 2147483647) -- eez impossible!
Run Code Online (Sandbox Code Playgroud)

对具有空索引的表运行不同版本的 proc:

CREATE OR ALTER PROCEDURE dbo.LargeUnusedGrant_alt1 (@OwnerUserId INT)
AS 
BEGIN

    SELECT TOP 200 *
    FROM dbo.Posts AS p
    WHERE p.OwnerUserId = @OwnerUserId
    AND p.PostTypeId = 1
    ORDER BY p.Score DESC, p.Id DESC;

END;
GO 
Run Code Online (Sandbox Code Playgroud)

使用相同的值:

EXEC dbo.LargeUnusedGrant_alt1 @OwnerUserId = 8672;
GO 
Run Code Online (Sandbox Code Playgroud)

首次运行:悲伤的记忆授予

第二轮:快乐记忆补助

多余的左连接:

/*Niko's Triko*/
CREATE OR ALTER PROCEDURE dbo.LargeUnusedGrant_alt2 (@OwnerUserId INT)
AS 
BEGIN

    CREATE TABLE #t1 (Id INT, INDEX cx_whatever CLUSTERED COLUMNSTORE);

    SELECT TOP 200 *
    FROM dbo.Posts AS p
    LEFT JOIN #t1 ON 1 = 1
    WHERE p.OwnerUserId = @OwnerUserId
    AND p.PostTypeId = 1
    ORDER BY p.Score DESC, p.Id DESC;

END;
GO 

EXEC dbo.LargeUnusedGrant_alt2 @OwnerUserId = 8672;
GO 
Run Code Online (Sandbox Code Playgroud)

首次运行:悲伤的记忆授予

第二轮:快乐记忆补助