执行计划中的位图创建导致聚集索引扫描的错误估计

SEa*_*986 6 sql-server execution-plan database-internals cardinality-estimates sql-server-2019

给定 StackOverflow2010 数据库上的以下简单查询:

SELECT  u.DisplayName,
        u.Reputation
FROM    Users u
        JOIN Posts p
            ON u.id = p.OwnerUserId
WHERE   u.DisplayName = 'alex' AND
        p.CreationDate >= '2010-01-01' AND
        p.CreationDate <= '2010-03-01'
Run Code Online (Sandbox Code Playgroud)

我试图理解为什么创建索引

CREATE INDEX IX_CreationDate ON Posts
(
    CreationDate
)
INCLUDE (OwnerUserId)
Run Code Online (Sandbox Code Playgroud)

产生更好的估计Posts.CreationDate

当我运行没有索引的查询时,我得到Plan 1。在此计划中,SQL Server 估计 Posts 上的 CI 扫描产生 298,910 行,实际上返回 552 行 - 这个估计值相差甚远。

添加索引后,我会得到Plan 2,这会导致索引查找和更准确的估计。

我很好奇为什么添加索引会导致更好的估计,因为当在谓词中使用列时会创建统计信息WHERE,无论它是否被索引。

经过进一步检查,我可以看到计划 1 与计划 2 上的谓词Posts.CreationDate不同:

计划 1 谓词

[StackOverflow2010].[dbo].[Posts].[CreationDate] as [p].[CreationDate]>='2010-01-01 00:00:00.000' AND [StackOverflow2010].[dbo].[Posts].[CreationDate] as [p].[CreationDate]<='2010-03-01 00:00:00.000' AND PROBE([Bitmap1002],[StackOverflow2010].[dbo].[Posts].[OwnerUserId] as [p].[OwnerUserId],N'[IN ROW]')
Run Code Online (Sandbox Code Playgroud)

计划 2 谓词

Seek Keys[1]: Start: [StackOverflow2010].[dbo].[Posts].CreationDate >= Scalar Operator('2010-01-01 00:00:00.000'), End: [StackOverflow2010].[dbo].[Posts].CreationDate <= Scalar Operator('2010-03-01 00:00:00.000')
Run Code Online (Sandbox Code Playgroud)

所以我可以看到计划 2 只是使用直方图来查找两个日期之间的行数,但计划 1 有一个稍微复杂的谓词,涉及位图探测。

这(我认为)解释了为什么对搜索的估计更准确,但我现在想知道什么是位图探针?我可以在计划中看到,创建了一个与 Alex 谓词匹配的用户 Id 的位图,这就是正在探测的内容。

我想知道“如果没有索引,为什么计划 1 与计划 2 不一样,唯一的区别是 CI 扫描而不是 CreationDate 上的索引查找?”

我做了一些进一步的测试,发现如果我在没有索引的情况下运行查询,但强制计划进行串行,OPTION (MAXDOP 1)我会得到计划 3,尽管现在对帖子进行 CI 扫描,但它对 CreationDate 有更好的估计。如果我查看谓词,我可以看到探测器现在消失了,并且位图不再在计划中,因此这使我相信位图与计划并行有关。

所以我的问题是 - 为什么当计划并行时会创建位图以及为什么它会导致如此糟糕的估计Posts.CreationDate

Pau*_*ite 9

有许多因素在起作用:

\n

该索引带有完整扫描统计信息。对自动创建的样本进行了采样。

\n

不同的基数估计模型和执行模式以不同的方式处理计算。在这种情况下,您可能会对使用原始 CE 模型进行的估计更满意:

\n
USE HINT (\'FORCE_LEGACY_CARDINALITY_ESTIMATION\')\n
Run Code Online (Sandbox Code Playgroud)\n

位图仅出现在行模式并行计划中。我在 Bitmap Magic 中写了详细信息(或\xe2\x80\xa6 SQL Server 如何使用位图过滤器)

\n

位图可以在串行和并行批处理模式计划中使用。您已将数据库设置为兼容模式 130,因此行存储上的批处理模式不可用。附注:您可能需要应用 2019\xe2\x80\x94 的最新 CU,但您仍使用 RTM。

\n

估计公式各不相同,但通常其根源在于使用直方图估计哈希连接构建端的过滤行与目标表之间的半连接。有时它是一种猜测。有时根本不考虑位图:

\n

在并行行模式计划中,有两种类型的位图。原始类型的位图是在查询优化完成后试探性添加的。由于它在优化期间不存在,因此它对基数估计没有影响。这些位图被命名为Bitmapxxxx. 你的是其中之一:

\n

位图

\n

由于位图的效果与CreationDate谓词混合在一起,因此更难以查看。我们可以使用未记录的跟踪标志 9130 将它们分开:

\n
SELECT\n    U.DisplayName,\n    U.Reputation\nFROM dbo.Users AS U\nJOIN dbo.Posts AS P\n    ON U.id = P.OwnerUserId\nWHERE\n    U.DisplayName = N\'alex\' \n    AND P.CreationDate >= CONVERT(datetime, \'2010-01-01\', 121)\n    AND P.CreationDate <= CONVERT(datetime, \'2010-03-01\', 121)\nOPTION \n(\n    USE HINT (\'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_130\'),\n    QUERYTRACEON 9130\n);\n
Run Code Online (Sandbox Code Playgroud)\n

位图在扫描时仍按行应用,但CreationDate上的谓词位于稍后的 Filter 运算符中:

\n

使用 TF 9130 进行扫描估算

\n

使用 TF 9130 进行滤波器估算

\n

扫描的估计是基表的完整基数,尽管位图仍然应用在那里:

\n

扫描时应用位图

\n

如果您有兴趣查看没有位图的计划来比较估计值,则可以启用未记录的跟踪标志 9498。

\n

第二种类型的行模式位图是所谓的优化位图。这些被评估为基于成本的优化的一部分,因此它们确实对基数估计和最终计划形状有影响这些位图被命名为Opt_Bitmapxxx.

\n

我在 SQL Server 中的批处理模式位图中写了有关批处理模式的详细信息。

\n