列存儲索引是好的!對於數據倉庫和報表工做量,它們是真正的性能加速器。與彙集列存儲結合,你會在常規行存儲索引(彙集索引,非彙集索引)上得到巨大的壓縮好處。並且建立彙集列存儲索引很是簡單:sql
CREATE CLUSTERED COLUMNSTORE INDEX ccsi ON TableName GO
但這是你對彙集列存儲須要知道的一切?並非,如你在這篇文章會看到的……app
在我各個研討會和公共培訓課程期間,我常常開玩笑:一旦你開釋使用匯集列存儲索引,你就不須要知道索引的更多信息。使用匯集列存儲索引很太多的優勢,它會帶來巨大的性能提高:性能
如你從下例子看到的,在SQL Server裏建立彙集列存儲索引很是簡單:優化
CREATE CLUSTERED COLUMNSTORE INDEX idx_ci ON FactOnlineSales GO
你只需指定表名,沒別的。甚至你不須要擔憂彙集鍵列,由於這個概念對列存儲索引不適用。很簡單,是否是?讓咱們在適當的地方用剛纔的彙集索引運行一個簡單的查詢:ui
-- 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:spa
但那些段讀取(Segment Read)和段跳過(Segment Skipped)度量呢?線程
大家也許知道列存儲索引內部分紅所謂的列存儲段(ColumnStore Segments)。一個列存儲段一般指定到特定的列和行組。一個行組包含近100萬行。下圖很好的展現了這個重要概念:code
來源:https://www.microsoft.com/en-us/research/publication/enhancements-to-sql-server-column-stores/server
這裏最重要的是,對於每一個列存儲段,SQL Server內部存儲了最小和最大的值。基於這些值,SQL Server能夠進行所謂的段消除。段消除意味着SQL Server只讀取包含請求數據的那些段(在訪問列存儲索引時)。你能夠認爲它是和分區消除同樣得方式,在你和分區表打交道的時候。但這裏的消除發生在列存儲段級別。
blog
如你在剛纔的圖片所見,在列存儲索引訪問期間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