智能查询处理功能详解

适用于:SQL ServerAzure SQL 数据库Azure SQL 托管实例Microsoft Fabric 中的 SQL 数据库

本文详细介绍了各种智能查询处理 (IQP) 功能、发行说明和更多详细信息。 智能查询处理 (IQP) 功能系列包含有广泛影响的功能,既能提升现有工作负荷的性能,还能最大限度地减少实现工作量。

可以通过为数据库启用适用的数据库兼容级别,使工作负载自动适用于智能查询处理。 可使用 Transact-SQL 进行此设置。 例如,要将数据库的兼容性级别设置为 SQL Server 2022 (16.x):

ALTER DATABASE [WideWorldImportersDW]
    SET COMPATIBILITY_LEVEL = 160;

有关新版本引入的更改的详细信息,请参阅:

批处理模式自适应联接

适用于:SQL Server(从 SQL Server 2017 (14.x) 开始)和 Azure SQL 数据库

批处理模式自适应联接功能通过使用单个缓存计划,可将哈希联接或嵌套循环联接方法的选择延迟到在扫描完第一个输入之后。 自适应联接运算符可定义用于决定何时切换到嵌套循环计划的阈值。 因此,你的执行计划可以在执行期间动态切换到更优的联接策略。

有关详细信息,包括如何在不更改兼容性级别的情况下禁用自适应连接,请参阅了解自适应连接

MSTVF 的交错执行

适用于:SQL Server(从 SQL Server 2017 (14.x) 开始)和 Azure SQL 数据库

多语句表值函数 (MSTVF) 是一种用户定义函数,可以接受参数、执行多个 T-SQL 语句和 RETURN 表。

交错执行有助于解决由于与 MSTVF 关联的固定基数估算而导致的工作负载性能问题。 通过交错执行,函数中的实际行计数可用于做出更明智的下游查询计划决策。

自 SQL Server 2014 (12.x) 起,MSTVF 的固定基数猜测为“100”,而早期 SQL Server 版本为“1”。

交错执行可更改单一查询执行的优化和执行阶段之间的单向边界,并使计划能够根据修订后的基数估值进行调整。 在优化过程中,如果数据库引擎遇到一个使用多语句表值函数 (MSTVF) 的交错执行候选项,优化将暂停,执行相应的子树,捕获准确的基数估计,然后恢复对下游操作的优化。

下图显示了实时查询统计信息输出结果,它是整个执行计划的一个子集,展示了 MSTVFs 的固定基数估计值所带来的影响

可查看实际行流与估计行数。 该方案中有三个值得注意的部分(流程从右向左):

  • MSTVF 表扫描的固定估计值为 100 行。 然而,在此示例中,有 527,597 行数据流经此 MSTVF 表扫描操作,如“实时查询统计信息”中显示的 527597 of 100 实际值与估计值之比所示 — 因此固定估计值存在显著偏差。
  • 对于嵌套循环操作,假设联接的外层仅返回 100 行数据。 鉴于 MSTVF 实际返回的行数极高,您可能最好完全采用另一种连接算法。
  • 对于 Hash Match 操作,请注意那个小警告图标,它在这种情况下表示发生了磁盘溢出。

执行计划中行流与估计行数的对比图。

将之前的计划与通过启用交替执行生成的实际计划进行对比:

交错执行计划的关系图。

  • MSTVF 表扫描现在反映了准确的基数估计。 另请注意该表扫描与其他操作的重排序。
  • 而关于联接算法,我们已改为从嵌套循环操作切换到哈希匹配操作,后者在涉及大量行时更优。
  • 另请注意,我们不再出现溢出警告,因为系统会根据 MSTVF 表扫描输出的实际行数分配更多内存。

符合交错执行条件的语句

在交错执行中引用 MSTVF 的语句目前必须为只读,且不能属于数据修改操作的一部分。 此外,如果 MSTVF 不使用运行时常量,则它们不符合交错执行的条件。

交错执行的优势

一般而言,估计行数与实际行数之间的偏差越大,再加上下游计划操作的数量越多,对性能的影响就越大。

通常,交错执行对以下情况的查询有益:

  • 中间结果集的估计行数与实际行数之间存在较大偏差(在本例中为 MSTVF)。

  • 整个查询对中间结果集规模的变化十分敏感。 通常情况下,当查询计划中某个子树上方存在一棵复杂的树时,就会发生这种情况。

    来自 MSTVF 的基本 SELECT * 无法从交错执行中获益。

交错执行的开销

开销应极少甚至没有。 MSTVF 已在引入交错执行前具体化,但区别是现在已允许延迟优化,并可使用具体化行集的基数估算。 与任何会引发变更的执行计划一样,某些计划可能会发生变化,导致子树的基数虽然更佳,但查询的整体执行计划却变差。 缓解可包含还原兼容级别或使用查询存储来强制计划的非回归版本。

交错执行和顺序执行

交错执行计划一旦被缓存,基于首次执行时修订后的估计值的计划就会用于后续执行,而无需重新实例化交错执行。

跟踪交错执行活动

可在实际查询执行计划中查看使用情况属性:

执行计划属性 Description
ContainsInterleavedExecutionCandidates 适用于 QueryPlan 节点。 当 true 时,表示该计划包含交错执行候选方案。
IsInterleavedExecuted RelOp 下 TVF 节点的 RuntimeInformation 元素的属性。 当 true 时,表示该操作已作为交错执行的一部分被实体化。

还可以通过以下扩展事件跟踪交错执行匹配项:

XEvent Description
interleaved_exec_status 当发生交错执行时,此事件会被触发。
interleaved_exec_stats_update 此事件描述了由交错执行更新的基数估计值。
Interleaved_exec_disabled_reason 当一个可能具备交错执行条件的查询实际上未采用交错执行时,此事件会被触发。

必须先执行查询,交错执行才能修订 MSTVF 的基数估计值。 不过,通过 ContainsInterleavedExecutionCandidates showplan 属性,估计的执行计划仍会显示存在交错执行候选方案。

交错式执行缓存

如果执行计划被清除或从缓存中移除,则在查询执行时会进行一次使用交错执行的新编译。 使用 OPTION (RECOMPILE) 的语句使用交错执行创建新计划,而不是将其缓存。

交错执行与查询存储的互操作性

可以使用交错执行的执行计划可以被强制执行。 该计划是基于初始执行结果修正了基数估计的版本。

在不更改兼容级别的情况下禁用交错执行

可在数据库或语句范围内禁用交错执行,同时将数据库兼容性级别维持在 140 或更高。 要禁用所有源自该数据库的查询执行中的交错执行,请在相应数据库的上下文中执行以下操作:

-- SQL Server 2017
ALTER DATABASE SCOPED CONFIGURATION SET DISABLE_INTERLEAVED_EXECUTION_TVF = ON;

-- Starting with SQL Server 2019, and in Azure SQL Database
ALTER DATABASE SCOPED CONFIGURATION SET INTERLEAVED_EXECUTION_TVF = OFF;

启用后,此设置在 sys.database_scoped_configurations 中将显示为已启用。 若要重新启用该数据库中所有查询执行的交错执行,请在相应数据库的上下文中执行以下操作:

-- SQL Server 2017
ALTER DATABASE SCOPED CONFIGURATION SET DISABLE_INTERLEAVED_EXECUTION_TVF = OFF;

-- Starting with SQL Server 2019, and in Azure SQL Database
ALTER DATABASE SCOPED CONFIGURATION SET INTERLEAVED_EXECUTION_TVF = ON;

此外,将 DISABLE_INTERLEAVED_EXECUTION_TVF 指定为 USE HINT 查询提示也可对特定查询禁用交错执行。 例如:

SELECT [fo].[Order Key],
       [fo].[Quantity],
       [fol].[OutlierEventQuantity]
FROM [Fact].[Order] AS [fo]
     INNER JOIN [Fact].[WhatIfOutlierEventQuantity]('Mild Recession', '1-01-2013', '10-15-2014') AS [fol]
         ON [fo].[Order Key] = [fol].[Order Key]
        AND [fo].[City Key] = [fol].[City Key]
        AND [fo].[Customer Key] = [fol].[Customer Key]
        AND [fo].[Stock Item Key] = [fol].[Stock Item Key]
        AND [fo].[Order Date Key] = [fol].[Order Date Key]
        AND [fo].[Picked Date Key] = [fol].[Picked Date Key]
        AND [fo].[Salesperson Key] = [fol].[Salesperson Key]
        AND [fo].[Picker Key] = [fol].[Picker Key]
OPTION (USE HINT('DISABLE_INTERLEAVED_EXECUTION_TVF'));

USE HINT 查询提示优先于数据库范围配置或跟踪标志设置。

标量 UDF 内联

适用于:SQL Server(从 SQL Server 2019 (15.x) 开始)和 Azure SQL 数据库

标量 UDF 内联会自动将标量 UDF 转换为关系表达式。 并将它们嵌入正在调用的 SQL 查询中。 此转换提升了利用标量 UDF 的工作负载的性能。 标量 UDF 内联有助于对 UDF 内部操作进行基于成本的优化。 结果是生成高效、面向集合且并行的执行计划,而非低效、迭代且串行的执行计划。 该功能在数据库兼容性级别为 150 或更高时默认启用。

有关详细信息,请参阅标量 UDF 内联

表变量延迟编译

适用于:SQL Server(从 SQL Server 2019 (15.x) 开始)和 Azure SQL 数据库

表变量延迟编译功能提升了计划质量和引用表变量的查询的整体性能。 在优化和初始计划编译期间,此功能传播基于实际表变量行计数的基数估算。 然后,此确切的行计数信息用于优化下游计划操作。

采用表变量延迟编译后,引用表变量的语句的编译将推迟到该语句首次实际执行时。 此延迟编译行为与临时表的行为相同。 此更改导致系统使用实际基数,而非最初的一行估计值。

要启用表变量延迟编译,请为查询运行时连接到的数据库启用数据库兼容性级别 150 或更高级别。

表变量延迟编译不会更改表变量的任何其他特性。 例如,此功能不会向表变量添加列统计信息。

表变量延迟编译不会增加重新编译频率。 而是,它改变了首次编译发生的位置。 生成的缓存执行计划基于初始延迟编译的表变量行数。 后续查询会重用缓存的计划。 该计划将被重复使用,直到被替换或重新编译为止。

用于初始计划编译的表变量行计数代表了一个典型值,这个值可能不同于固定行数的假设。 如果存在差异,下游操作将受益。 如果表变量行计数在整个执行过程中差别很大,则可能无法通过此功能来提升性能。

在不更改兼容性级别的情况下禁用表变量延迟编译

可在数据库或语句范围内禁用表变量延迟编译,同时将数据库兼容性级别维持在 150 或更高。 要禁用数据库中所有查询执行的表变量延迟编译,请在相应数据库的上下文中执行以下示例:

ALTER DATABASE SCOPED CONFIGURATION SET DEFERRED_COMPILATION_TV = OFF;

若要对源自数据库的所有查询重新启用表变量延迟编译,请在对应数据库的上下文中执行以下示例:

ALTER DATABASE SCOPED CONFIGURATION SET DEFERRED_COMPILATION_TV = ON;

您还可以通过将 DISABLE_DEFERRED_COMPILATION_TV 作为 USE HINT 查询提示进行赋值,来禁用特定查询的表变量延迟编译。 例如:

DECLARE @LINEITEMS TABLE (
    L_OrderKey INT NOT NULL,
    L_Quantity INT NOT NULL);

INSERT @LINEITEMS
SELECT L_OrderKey,
       L_Quantity
FROM dbo.lineitem
WHERE L_Quantity = 5;

SELECT O_OrderKey,
       O_CustKey,
       O_OrderStatus,
       L_QUANTITY
FROM ORDERS, @LINEITEMS
WHERE O_ORDERKEY = L_ORDERKEY
      AND O_OrderStatus = 'O'
OPTION (USE HINT('DISABLE_DEFERRED_COMPILATION_TV'));

参数敏感度计划优化

适用于: SQL Server 2022 (16.x) 及更高版本 Azure SQL 数据库Azure SQL 托管实例

参数敏感度计划 (PSP) 优化是智能查询处理系列功能的一部分。 它解决了参数化查询的单个缓存计划对于所有可能的传入参数值都不是最佳方案的情况。 这是数据分布不均匀的情况。

近似查询处理

近似查询处理是新的功能系列。 它可以跨响应速度比绝对精度更为关键的大型数据集进行聚合。 例如,要跨 100 亿行计算 COUNT(DISTINCT()),以供显示在仪表板上。 在这种情况下,绝对精度并不重要,而响应速度则至关重要。

非重复近似计数

适用于:SQL Server(从 SQL Server 2019 (15.x) 开始)和 Azure SQL 数据库

新的 APPROX_COUNT_DISTINCT 聚合函数返回组中非 NULL 唯一值的近似数量。

从 SQL Server 2019 (15.x) 开始,无论兼容性级别如何,此功能均可用。

有关详细信息,请参阅 APPROX_COUNT_DISTINCT

近似百分位数

适用于:SQL Server(从 SQL Server 2022 (16.x) 开始)和 Azure SQL 数据库

这些聚合函数可计算具有基于排名的可接受误差范围的大型数据集的百分位数,以帮助使用近似百分位数聚合函数快速做出决策。

有关详细信息,请参阅 APPROX_PERCENTILE_DISCAPPROX_PERCENTILE_CONT

行存储中的批处理模式

适用于:SQL Server(从 SQL Server 2019 (15.x) 开始)和 Azure SQL 数据库

行存储上的批处理模式可实现分析工作负载的批处理模式执行,而无需列存储索引。 此功能支持批处理模式,并支持用于磁盘堆和 B 树索引的位图筛选器。 行存储上的批处理模式可实现对所有现有支持批处理模式的运算符的支持。

可从行存储上的批处理模式中受益最多的现有查询包括:

  • 大型行存储表之间的哈希联接
  • 包含 GROUP BY 且涉及大量不同值的查询
  • 聚合函数,例如SUMCOUNTMINMAXAVG
  • 包含 OVERPARTITION BYORDER BY(包括聚合函数、ROW_NUMBERRANK)的窗口函数查询

有关窗口函数性能的详细信息,请参阅子句参考中的OVER

Note

文档在提到索引时一般使用 B 树这个术语。 在行存储索引中,数据库引擎实现了 B+ 树。 这不适用于列存储索引或内存优化表上的索引。 有关详细信息,请参阅 SQL Server 以及 Azure SQL 索引体系结构和设计指南

批模式执行概述

SQL Server 2012 (11.x) 引入了一项可加速分析工作负载的新功能,即列存储索引。 在 SQL Server 的每个后续版本中,列存储索引的用例和性能都有所提高。 在表上创建列存储索引可以提高分析工作负载的性能。 不过,有两套相关但不同的技术:

  • 使用列存储索引,分析查询仅访问所需列中的数据。 与传统 rowstore 索引中的压缩相比,列存储格式中的页压缩也更有效。
  • 使用批处理模式处理,查询运算符可以更高效地处理数据。 它们对一组行进行处理,而不是逐行处理。 许多其他可伸缩性改进都与批处理模式处理相关。 若要详细了解批处理模式,请参阅执行模式

两组功能协同工作,以改进输入/输出 (I/O) 和 CPU 利用率:

  • 使用列存储索引后,你的更多数据可装入内存。 这会减少 I/O 工作负荷。
  • 批处理模式处理可更有效地使用 CPU。

这两种技术尽可能利用彼此。 例如,批处理模式聚合可以作为列存索引扫描的一部分进行计算。 此外,使用批处理模式连接和批处理模式聚合时,经过压缩的列存储数据通过运行长度编码进行处理,效率会更高。

但是,必须了解这两个功能是独立的:

  • 您可以获得使用列存储索引的行模式执行计划。
  • 你可以获取仅使用行存储索引的批处理模式计划。

结合使用这两种功能时,通常效果最佳。 在 SQL Server 2019 (15.x) 之前,SQL Server 查询优化器仅对涉及至少一个有列存储索引的表的查询考虑使用批处理模式处理。

列存储索引可能不适用于某些应用程序。 应用程序可能会使用列存储索引不支持的其他一些功能。 例如,直接在原地进行的修改与列存储压缩不兼容。 因此,带有聚集列存储索引的表不支持触发器。 更重要的是,列存储索引会给 DELETEUPDATE 语句增加额外开销。

对于一些事务与分析混合工作负荷,事务工作负荷的开销超过了使用列存储索引的获益。 此类方案可以通过仅使用批处理模式处理来提高 CPU 使用率,以从中获益。 这就是为什么无论涉及哪种索引类型,行存储上的批处理模式功能都会考虑所有查询的批处理模式。

可能从行存储的批处理模式中受益的工作负载

以下工作负载可能会受益于行存储上的批处理模式:

  • 工作负载的很大一部分由分析查询组成。 通常情况下,这些查询使用联接或聚合等运算符,可处理数十万行或更多行。
  • 工作负载受 CPU 限制。 如果瓶颈是 I/O,仍建议您在可能的情况下考虑使用列存储索引。
  • 创建列存储索引会给工作负载中的事务处理部分带来过多开销。 或者,创建列存储索引不可行,因为应用程序依赖于列存储索引尚不支持的功能。

Note

行存储上的批处理模式仅通过降低 CPU 消耗来发挥作用。 如果瓶颈与 I/O 相关,并且数据尚未缓存(“冷”缓存),则行存储上的批处理模式不会改善查询运行时间。 同样,如果计算机上没有足够的内存来缓存所有数据,则性能改进不太可能。

启用行存储上的批处理模式后,会发生哪些变化?

行存储上的批处理模式要求数据库的兼容性级别为 150。

即使查询无法访问具有列存储索引的任何表,查询处理器也使用启发式来决定是否考虑批处理模式。 启发式规则包括以下检查:

  1. 对输入查询中的表大小、所用运算符和估计基数进行初步检查。
  2. 其他检查点,因为优化器会发现新的、成本更低的查询计划。 如果这些替代计划没有大量使用批处理模式,优化器会停止探索批处理模式替代方案。

如果使用行存储上的批处理模式,则会发现在查询计划中实际运行模式为批处理模式。 扫描操作符对磁盘堆和 B-树索引使用批处理模式。 此批处理模式扫描可以评估批处理模式位图筛选器。 你可能还会在执行计划中看到其他批处理模式操作符。 例如,哈希联接、基于哈希的聚合、排序、窗口聚合、筛选器、串联和计算标量运算符。

Remarks

查询计划并不总是使用批处理模式。 查询优化器可能会认为批处理模式对查询没有益处。

查询优化器搜索空间正在发生变化。 因此,如果您获得的是行模式执行计划,它可能与较低兼容性级别下生成的执行计划不同。 另外,如果你得到的是批处理模式执行计划,它可能与你在使用列存储索引时得到的执行计划不同。

对于混合使用列存储索引和行存储索引的查询,由于新的行存储批处理模式扫描,查询计划也可能会发生变化。

用于行存储扫描的新批处理模式当前存在以下限制:

  • 对于内存中的 OLTP 表,或除磁盘堆和 B-树以外的任何索引,该机制均不会生效。
  • 此外,如果要检索或筛选大对象 (LOB) 列,该机制也不会生效。 此限制包括稀疏列集和 XML 列。

有些查询即使使用列存储索引,也无法使用批处理模式。 例如,涉及游标的查询。 这些相同的排除情况同样适用于行存储上的批处理模式。

对行存储配置批处理模式

BATCH_MODE_ON_ROWSTORE 数据库范围的配置默认为 ON。

无需更改数据库兼容性级别,即可禁用行存储上的批处理模式:

-- Disabling batch mode on rowstore
ALTER DATABASE SCOPED CONFIGURATION SET BATCH_MODE_ON_ROWSTORE = OFF;

-- Enabling batch mode on rowstore
ALTER DATABASE SCOPED CONFIGURATION SET BATCH_MODE_ON_ROWSTORE = ON;

可以通过数据库范围的配置来禁用行存储上的批处理模式。 但仍可使用 ALLOW_BATCH_MODE 查询提示,在查询一级替代设置。 以下示例启用行存储的批处理模式,即使通过数据库作用域配置禁用了该功能:

SELECT [Tax Rate],
       [Lineage Key],
       [Salesperson Key],
       SUM(Quantity) AS SUM_QTY,
       SUM([Unit Price]) AS SUM_BASE_PRICE,
       COUNT(*) AS COUNT_ORDER
FROM Fact.OrderHistoryExtended
WHERE [Order Date Key] <= DATEADD(dd, -73, '2015-11-13')
GROUP BY [Tax Rate], [Lineage Key], [Salesperson Key]
ORDER BY [Tax Rate], [Lineage Key], [Salesperson Key]
OPTION (RECOMPILE, USE HINT('ALLOW_BATCH_MODE'));

还可以使用 DISALLOW_BATCH_MODE 查询提示,对特定查询禁用行存储上的批处理模式。 请参阅以下示例:

SELECT [Tax Rate],
       [Lineage Key],
       [Salesperson Key],
       SUM(Quantity) AS SUM_QTY,
       SUM([Unit Price]) AS SUM_BASE_PRICE,
       COUNT(*) AS COUNT_ORDER
FROM Fact.OrderHistoryExtended
WHERE [Order Date Key] <= DATEADD(dd, -73, '2015-11-13')
GROUP BY [Tax Rate], [Lineage Key], [Salesperson Key]
ORDER BY [Tax Rate], [Lineage Key], [Salesperson Key]
OPTION (RECOMPILE, USE HINT('DISALLOW_BATCH_MODE'));

查询处理反馈功能

查询处理反馈功能是智能查询处理系列功能的一部分。

查询处理反馈是 SQL Server、Azure SQL 数据库和 Azure SQL 托管实例中的查询处理器使用有关查询执行的历史数据来决定查询是否可以从编译和执行方式的一个或多个更改中获得帮助的过程。 性能数据在查询存储收集,并提出了各种改进查询执行的建议。 如果成功,我们会将这些修改持久保存到磁盘、内存和/或 查询存储 中,以供后续使用。 如果建议没有带来足够的改进,它们就会被丢弃,查询将在没有反馈的情况下继续执行。

有关 SQL Server 的不同版本或 Azure SQL 数据库或 Azure SQL 托管实例中提供哪些查询处理反馈功能的信息,请参阅 SQL 数据库中的智能查询处理,或有关每个反馈功能的以下文章。

内存授予反馈

内存授予反馈已在 SQL Server 过去几个主要版本中陆续引入。

批处理模式内存授予反馈

有关批处理模式内存授予反馈的信息,请访问批处理模式内存授予反馈

行模式内存授予反馈

有关行模式内存授予反馈的信息,请访问行模式内存授予反馈

百分位数和持久性模式内存授予反馈

有关百分位数和持久性模式内存授予反馈的信息,请访问百分位数和持久性模式内存授予反馈

并行处理度 (DOP) 反馈

有关 DOP 反馈的信息,请访问并行度 (DOP) 反馈

基数估计 (CE) 反馈

有关 CE 反馈的信息,请访问基数估计 (CE) 反馈

使用查询存储优化计划强制

有关查询存储强制优化计划的信息,请访问查询存储的优化计划强制