執行計劃中的點陣圖創建導致聚集索引掃描的錯誤估計
鑑於 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'
我試圖理解為什麼要創建索引
CREATE INDEX IX_CreationDate ON Posts ( CreationDate ) INCLUDE (OwnerUserId)
產生更好的估計
Posts.CreationDate
當我執行沒有索引的查詢時,我得到Plan 1。在這個計劃中,SQL Server 估計有 298,910 行來自對 Posts 的 CI 掃描,實際上有 552 行回來了——這個估計還有很長的路要走。
添加索引後,我會得到Plan 2,這會導致索引搜尋和更準確的估計。
我很好奇為什麼添加索引會導致更好的估計,因為在謂詞中使用列時會創建統計資訊
WHERE
,無論它是否被索引。進一步檢查,我可以看到
Posts.CreationDate
計劃 1 和計劃 2 的謂詞不同:計劃 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]')
計劃 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')
所以我可以看到計劃 2 只是要使用直方圖來查找兩個日期之間的行數,但計劃 1 有一個稍微複雜的謂詞,涉及點陣圖探測。
這(我認為)解釋了為什麼對搜尋的估計更準確,但我現在想知道什麼是點陣圖探測?我可以在計劃中看到創建了一個與 Alex 謂詞匹配的使用者 ID 的點陣圖,這就是正在調查的內容。
我想知道“沒有索引,為什麼計劃 1 與計劃 2 不同,唯一的區別是 CI 掃描而不是 CreationDate 上的索引搜尋?”
我做了一些進一步的測試,發現如果我在沒有索引的情況下執行查詢但強制計劃進入串列,使用
OPTION (MAXDOP 1)
我得到計劃 3,儘管現在對 Posts 進行 CI 掃描,但它對 CreationDate 的估計更好。如果我查看謂詞,我可以看到探針現在已經消失並且點陣圖不再在計劃中,因此這使我相信點陣圖與計劃並行有關。所以我的問題是 - 為什麼在計劃並行時會創建點陣圖,為什麼會導致對 的估計如此糟糕
Posts.CreationDate
?
許多因素在起作用:
該索引帶有完整的掃描統計資訊。對自動創建的進行採樣。
不同的基數估計模型和執行模式以不同的方式處理計算。在這種情況下,您可能對使用原始 CE 模型的估計更滿意:
USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION')
點陣圖僅出現在行模式並行計劃中。我在Bitmap Magic中寫過詳細資訊(或者……SQL Server 如何使用點陣圖過濾器)
點陣圖在串列和並行批處理模式計劃中都是可能的。您將數據庫設置為兼容模式 130,因此行儲存上的批處理模式對您不可用。旁注:您可能想要應用 2019 年的最新 CU——您仍在使用 RTM。
估計公式各不相同,但通常其根源在於使用直方圖估計雜湊連接的建構端的過濾行和目標表之間的半連接。有時它是一種猜測。有時根本不考慮點陣圖:
在並行行模式計劃中,有兩種類型的點陣圖。原始類型的點陣圖是在查詢優化完成後啟發式添加的。由於它在優化期間不存在,因此對基數估計**沒有影響。**這些點陣圖被命名為
Bitmapxxxx
. 你的就是其中之一:由於點陣圖的效果與CreationDate謂詞混合在一起,因此更難看到。我們可以使用未記錄的跟踪標誌 9130 將它們分開:
SELECT U.DisplayName, U.Reputation FROM dbo.Users AS U JOIN dbo.Posts AS P ON U.id = P.OwnerUserId WHERE U.DisplayName = N'alex' AND P.CreationDate >= CONVERT(datetime, '2010-01-01', 121) AND P.CreationDate <= CONVERT(datetime, '2010-03-01', 121) OPTION ( USE HINT ('QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_130'), QUERYTRACEON 9130 );
點陣圖在掃描時仍然在行內應用,但CreationDate上的謂詞在後面的 Filter 運算符中:
對掃描的估計是基表的完整基數,儘管點陣圖仍然在那裡應用:
如果您有興趣查看沒有點陣圖來比較估計值的計劃,您可以啟用未記錄的跟踪標誌 9498。
第二種類型的行模式點陣圖是所謂的優化點陣圖。這些被評估為基於成本的優化的一部分,因此它們確實對基數估計和最終計劃形狀有影響。這些點陣圖被命名為
Opt_Bitmapxxx
.我在 SQL Server 的 Batch Mode Bitmaps 中寫了有關批處理模式的詳細資訊。