列存儲索引是好的!對于數據倉庫和報表工作量,它們是真正的性能加速器。與聚集列存儲結合,你會在常規行存儲索引(聚集索引,非聚集索引)上獲得巨大的壓縮好處。而且創建聚集列存儲索引非常簡單:
CREATE CLUSTERED COLUMNSTORE INDEX ccsi ON TableName GO
但這是你對聚集列存儲需要知道的一切?并不是,如你在這篇文章會看到的……
什么是列存儲段(ColumnStore Segments)?
在我各個研討會和公共培訓課程期間,我經常開玩笑:一旦你開釋使用聚集列存儲索引,你就不需要知道索引的更多信息。使用聚集列存儲索引很太多的優點,它會帶來巨大的性能提升:
- 更好的壓縮
- 批處理模式執行
- 更少I/O,更好內存管理
- 段消除
如你從下例子看到的,在SQL Server里創建聚集列存儲索引非常簡單:
CREATE CLUSTERED COLUMNSTORE INDEX idx_ci ON FactOnlineSales GO
你只需指定表名,沒別的。甚至你不需要擔心聚集鍵列,因為這個概念對列存儲索引不適用。很簡單,是不是?讓我們在適當的地方用剛才的聚集索引運行一個簡單的查詢:
-- Segment Elimination doesn't work quite well, because -- we have a lot of overlapping Segments. SELECT DateKey, SUM(SalesAmount) FROM FactOnlineSales_Temp WHERE DateKey >= '20090101' AND DateKey <= '20090131' GROUP BY DateKey GO
這個查詢非常快,因為對于查詢執行,SQL Server可以使用聚集列存儲索引。從STATISTICS IO輸出也向你展示了,對于聚集列存儲索引不需要很多LOB Logical Reads:
但那些段讀取(Segment Read)和段跳過(Segment Skipped)度量呢?
你們也許知道列存儲索引內部分成所謂的列存儲段(ColumnStore Segments)。一個列存儲段通常指定到特定的列和行組。一個行組包含近100萬行。下圖很好的展示了這個重要概念:
來源:https://www.microsoft.com/en-us/research/publication/enhancements-to-sql-server-column-stores/
什么是列存儲段消除(ColumnStore Segment Elimination)?
這里最重要的是,對于每個列存儲段,SQL Server內部存儲了最小和最大的值。基于這些值,SQL Server可以進行所謂的段消除。段消除意味著SQL Server只讀取包含請求數據的那些段(在訪問列存儲索引時)。你可以認為它是和分區消除一樣得方式,在你和分區表打交道的時候。但這里的消除發生在列存儲段級別。
如你在剛才的圖片所見,在列存儲索引訪問期間SQL Server不能消除任何段,因為默認情況下,在列存儲索引里你沒有排列順序。你數據的排列順序取決于在執行計劃里,在你創建列存儲索引時,SQL Server如何讀取數據:
如你所見,聚集列存儲索引通過從最初包含數據的堆表創建。因此在聚集列存儲索引里,你沒有排列順序,因此段消除不能很好為你工作。
如何改善情況?在你的數據里首先通過創建傳統的行存儲聚集索引來強制排序,然后修改它為聚集列存儲索引!偶滴神啊……
-- Now we create a traditional RowStore Clustered Index to sort our -- table data by the column "DateKey". CREATE CLUSTERED INDEX idx_ci ON FactOnlineSales_Temp(DateKey) GO -- "Swap" the Clustered Index through a Clustered ColumnStore Index CREATE CLUSTERED COLUMNSTORE INDEX idx_ci ON FactOnlineSales_Temp WITH (DROP_EXISTING = ON) GO
有了傳統的聚集行存儲索引就位,當你創建聚集列存儲索引時,在執行計劃里,查詢優化器會引用這個索引:
作為副作用,在聚集列存儲索引里,你現在應該有已排序的數據,段消除應該會很好處理:
-- Segment Elimination works better than previously, but still not perfectly. SELECT DateKey, SUM(SalesAmount) FROM FactOnlineSales_Temp WHERE DateKey >= '20090101' AND DateKey <= '20090131' GROUP BY DateKey GO
但當你再次查看STATISTICS IO的輸出,SQL Server還是需要讀取很多段,只跳過其中幾個:
但為什么SQL Server不能跳過所有的段而只跳過幾個?問題存在于聚集列存儲的創建。當你回頭看剛才的執行計劃,你會看到ColumnStore Index Insert (Clustered) 運算符是并行運行的——通過多個工作者線程。而且這些工作者線程再次破壞了聚集列存儲索引里你數據的排序!你從聚集行存儲索引里進行你的數據讀取,然后聚集列索引的并行創建重排了你的數據……傷及無辜~~~
你只能通過使用MAXDOP為1的聚集列存儲創建來解決這個問題:
CREATE CLUSTERED COLUMNSTORE INDEX idx_ci ON FactOnlineSales_Temp WITH (DROP_EXISTING = ON, MAXDOP = 1) GO
這聽起來很糟糕,事實也如此!但這是唯一讓你在列存儲索引里阻止重排你數據的解決方法。當你接下來從聚集列存儲數據讀取后,你會看到SQL Server終于能跳過所有的段:
小結
聚集列存儲索引很好——真的很好!但默認段消除不能很好進行,因為在你的聚集列存儲里沒有預定義的排序。因此在你調優你的列存儲查詢時,你要確保段消除可以正常進行。而且有時候你甚至需要通過使用MAXDOP 1來阻止你的數據排序……
感謝關注!
原文鏈接:
https://www.sqlpassion.at/archive/2017/01/30/columnstore-segment-elimination
不含病毒。www.avast.com |
留言列表