文章出處

列存儲索引是好的!對于數據倉庫和報表工作量,它們是真正的性能加速器。與聚集列存儲結合,你會在常規行存儲索引(聚集索引,非聚集索引)上獲得巨大的壓縮好處。而且創建聚集列存儲索引非常簡單:

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
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 AutoPoster 的頭像
    AutoPoster

    互聯網 - 大數據

    AutoPoster 發表在 痞客邦 留言(0) 人氣()