為聚合使用索引視圖 - 好得令人難以置信?
我們有一個記錄數相當大(10-2000 萬行)的數據倉庫,並且經常執行對特定日期之間的記錄進行計數的查詢,或者對具有特定標誌的記錄進行計數,例如
SELECT f.IsFoo, COUNT(*) AS WidgetCount FROM Widgets AS w JOIN Flags AS f ON f.FlagId = w.FlagId WHERE w.Date >= @startDate GROUP BY f.IsFoo
性能並不糟糕,但可能相對緩慢(在冷記憶體上可能需要 10 秒)。
最近我發現我可以
GROUP BY
在索引視圖中使用,所以嘗試了類似於以下的東西CREATE VIEW TestView WITH SCHEMABINDING AS SELECT Date, FlagId, COUNT_BIG(*) AS WidgetCount FROM Widgets GROUP BY Date, FlagId; GO CREATE UNIQUE CLUSTERED INDEX PK_TestView ON TestView ( Date, FlagId );
結果,我的第一個查詢的性能現在 < 100 毫秒,結果視圖和索引 < 100k(儘管我們的行數很大,但日期和標誌 ID 的範圍意味著該視圖僅包含 1000-2000 行)。
我認為這可能會削弱寫入 Widget 表的性能,但不會 - 據我所知,插入和更新該表的性能幾乎不受影響(另外,作為一個數據倉庫,該表不經常更新反正)
對我來說,這似乎好得令人難以置信——是嗎?以這種方式使用索引視圖時需要注意什麼?
正如您所指出的,視圖本身僅實現了少量行 - 因此即使您更新整個表,更新視圖所涉及的*額外I/O 也可以忽略不計。*當您創建視圖時,您可能已經感受到了最大的痛苦。下一個最接近的情況是,如果您向基表中添加大量行,其中包含一堆需要視圖中的新行的新 ID。
這不太好,不可能是真的。您正在使用索引視圖的用途,或者至少是最有效的方法之一:在寫入時為未來的查詢聚合支付費用。當結果遠小於源時,當然,當請求聚合的頻率高於更新基礎數據的頻率時(通常在 DW 中比在 OLTP 中更常見),這種方法效果最好。
不幸的是,許多人認為索引視圖很神奇 - 索引不會使所有視圖更高效,尤其是簡單地連接表和/或生成與源相同數量的行(甚至相乘)的視圖。在這些情況下,視圖中的 I/O 與原始查詢相同甚至更差,不僅因為存在相同或更多的行,而且它們通常還儲存和實現更多列。因此,提前實現這些並不會帶來任何收益,因為即使使用 SSD,I/O、網路和客戶端處理/渲染仍然是向客戶端返回大型結果集的主要瓶頸。與您仍在使用的所有其他資源相比,您在執行時避免加入所獲得的節省是無法衡量的。
與非聚集索引一樣,請注意不要過度使用。如果您將 10 個不同的索引視圖添加到一張表中,您將看到對工作負載的寫入部分的影響更大,尤其是在分組列不是(在)集群鍵的情況下。
天哪,我一直想寫關於這個話題的部落格。