SQL Server优化过程揭秘

Rad*_*ača 6 sql-server optimization

我们希望看到 SQL Server 优化器在查询优化期间考虑的查询计划的所有变体。SQL Server 使用querytraceon选项提供了非常详细的洞察力。例如,QUERYTRACEON 3604, QUERYTRACEON 8615允许我们打印出 MEMO 结构并QUERYTRACEON 3604, QUERYTRACEON 8619打印出在优化过程中应用的转换规则列表。这很好,但是,我们在跟踪输出方面有几个问题:

  1. 似乎 MEMO 结构只包含查询计划的最终变体或后来被重写为最终变体的变体。有没有办法找到“不成功/没有希望”的查询计划?
  2. MEMO 中的运算符不包含对 SQL 部分的引用。例如,LogOp_Get 运算符不包含对特定表的引用。
  3. 转换规则不包含对 MEMO 运算符的精确引用,因此,我们无法确定转换规则转换了哪些运算符。

让我用一个更详细的例子来展示它。让我有两个人造表AB

WITH x AS (
        SELECT n FROM
        (
            VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)
        ) v(n)
    ),
    t1 AS
    (
        SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n + 10000 * tenthousands.n + 100000 * hundredthousands.n as id  
        FROM x ones, x tens, x hundreds, x thousands, x tenthousands, x hundredthousands
    )
SELECT
    CAST(id AS INT) id, 
    CAST(id % 9173 AS int) fkb, 
    CAST(id % 911 AS int) search, 
    LEFT('Value ' + CAST(id AS VARCHAR) + ' ' + REPLICATE('*', 1000), 1000) AS padding
INTO A
FROM t1;


WITH x AS (
        SELECT n FROM
        (
            VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)
        ) v(n)
    ),
    t1 AS
    (
        SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n AS id  
        FROM x ones, x tens, x hundreds, x thousands       
    )
SELECT
    CAST(id AS INT) id,
    CAST(id % 901 AS INT) search, 
    LEFT('Value ' + CAST(id AS VARCHAR) + ' ' + REPLICATE('*', 1000), 1000) AS padding
INTO B
FROM t1;
Run Code Online (Sandbox Code Playgroud)

现在,我运行一个简单的查询

SELECT a1.id, a1.fkb, a1.search, a1.padding
FROM A a1 JOIN A a2 ON a1.fkb = a2.id
WHERE a1.search = 497 AND a2.search = 1
OPTION(RECOMPILE, 
    MAXDOP 1,
    QUERYTRACEON 3604,
    QUERYTRACEON 8615)
    
Run Code Online (Sandbox Code Playgroud)

我得到了相当复杂的输出,它描述了具有 15 个组的 MEMO 结构(您可以自己尝试)。这是使用树可视化 MEMO 结构的图片。 在此处输入图片说明 从树中可以观察到在优化器找到最终查询计划之前应用了某些规则。例如join commute( JoinCommute)、join to hash join( JNtoHS) 或Enforce sort( EnforceSort)。如前所述,可以使用QUERYTRACEON 3604, QUERYTRACEON 8619选项打印出优化器应用的整套重写规则。问题:

  1. 我们可能会在 8619 列表中找到JNtoSM( Join to sort merge) 重写规则,但是,排序合并运算符不在 MEMO 结构中。我知道排序合并的成本可能更高,但为什么它不在 MEMO 中?
  2. 如何知道LogOp_GetMEMO 中的运算符是否引用了表 A 或表 B?
  3. 如果我GetToIdxScan - Get -> IdxScan在 8619 列表中看到规则,如何将其映射到 MEMO 运算符?

关于此的资源数量有限。我已经阅读了许多关于转换规则和 MEMO 的 Paul White 博客文章,但是,上述问题仍未得到解答。谢谢你的帮助。

Fra*_*ani 0

我将尝试回答您的问题:

1. MEMO 结构似乎只包含查询计划的最终变体或后来重写为最终计划的变体。有没有办法找到“不成功/没有希望”的查询计划?

不,遗憾的是没有办法做到这一点。@Ronaldo 在评论中粘贴了一个很好的链接。我的建议是使用Include Live Query Statistics

在此输入图像描述

并尝试找出是否看到不同的查询计划。使用top 10top 1000、 或*,您将看到将建议不同的查询计划。您还可以使用query hint并强制您的查询计划采用不同的模式。基本上“做你自己废弃的查询计划”

2. MEMO 中的运算符不包含对 SQL 部分的引用。例如,LogOp_Get 运算符不包含对特定表的引用。

使用QUERYTRACEON 8605,我可以看到对该表的引用:

在此输入图像描述

3. 转换规则不包含对 MEMO 算子的精确引用,因此,我们无法确定转换规则转换了哪些算子

GetToIdxScan - Get -> IdxScan我在您提供的查询中没有看到任何内容。我的建议是使用 UseQUERYTRACEON 8605QUERYTRACEON 8606,那里应该有一个参考。

编辑:

那么“...是否可以在 SQL Server 中查看有关候选计划的更多信息。”

答案是否定的,因为没有其他候选查询计划。事实上,一个常见的误解是 SQL Server 会返回给您最佳的查询计划。SQL Server 根本无法为您计算所有可能的解决方案:这将需要...我不知道...分钟...?小时...?计算每一个解决方案是不可行的。

但是,如果您想调查为什么查询计划选择该模式,您可以使用:

  • SET SHOWPLAN_ALL ON: SQL Server 将返回查询计划的每个计算的逻辑树

在此输入图像描述

  • DBCC SHOW_STATISTICS('A', 'PK_A'):这将显示有关目标表和约束的统计信息。我创建了一个键来向您显示结果,如果您的表被更频繁地查询,您自然会看到更多信息

在此输入图像描述

  • USE HINT('force_legacy_cardinality_estimation'):将允许您使用以前的基数估计,因此您可以检查使用旧基数估计您的查询计划是否可能更快。