SQL Server 列存儲索引性能總結(11)——列存儲的維護

接上文:SQL Server 列存儲索引性能總結(10)——行組的大小影響索引需要維護,這個不多說,而維護通常就是兩種:重建和重組。在一些可控的環境下(比如自己的機房),我選擇使用Ola Hallengren的一整套維護腳本。非常好用。不過對於類似PaaS平臺的SQL Server,可控性很弱,可能需要自己開發。但是這個不是本文的主題。本文介紹並演示重組和重建。

如何選擇重組和重建

重組

   傳統索引也就是行存儲索引,通過移動數據的位置到儘可能少的數據頁中,從而達到減少因爲增刪改帶來的碎片。
   對於列存儲索引來說需要引入Tuple Mover,默認行爲中它用於把Close的行組編碼並壓縮成片段。也可以通過COMPRESS_ALL_ROW_GROUPS = ON這個hint來調用Tuple Mover尋找任何open的Delta-Stores,然後馬上關閉它們並壓縮。

重建

   行存儲索引的重建會重新整理索引的指針,並且釋放沒使用的空間等。如果對行存儲進行壓縮,可以使用ROW/PAGE兩種壓縮選項。
   列存儲的過程基本上一樣的,不過一個點是順序不能保證。

選擇的考慮點

   爲了省事,我過去幾乎全用重建,只要有足夠的維護時間窗口,問題是不大的。但是作爲技術人員,還是有必要具體問題具體分析。
   在考慮使用何種操作時,其中一個關鍵點就是刪除的數據量及其所在的行組。如果一個表的片段有大概50%被刪除,那麼值得重建。另外比如有個表有2百萬行數,分佈在2個片段中,然後其中100萬行已經被標識爲已刪除,如果這100萬行數是屬於同一個行組,還是屬於多個行組,當涉及到影響片段消除(後面再說),則需要考慮重建。
   由於在片段中,數據是無序的(壓縮過後已經很難分出某個值對應的數據的順序),任何行的刪除都會被認爲導致列存儲的碎片。對於行存儲來說,特別是聚集行存儲索引,索引指針是有序的,這個時候在順序的最大值處進行增加和刪除並不會造成明顯的碎片。
   接下來的實驗中,只對片段進行演示分析和處理,畢竟這是列存儲索引的主要結構。Delta Store使用B-Tree也就是行存儲結構,並且內部是以Heap來表示,所以實際上是無序的,同時Delta Store裏面的數據很容易就被刪除,所以不應該列入考慮範圍。
   在開始實驗之前,先定義碎片類型:

  • 片段碎片:特定壓縮片段的總行數與已經刪除的行數的比例。注意是特定片段,而不是整個列存儲索引。
  • 地盤碎片的大小:列存儲索引的大小,因爲每個片段的大小可能差異很大,所以不做細分。

   如果要做個準確的決定,使用rebuild時:

  • 30%:分區或表的總碎片率。
  • 10%:列存儲表或分區中,10%的數據被完全刪除。

   下面開始使用ContosoRetailDW作爲演示。

演示

   本文演示使用FactOnlineSales, FactSales, FactInventory 和 FactSalesQuota四個表,下面先清理約束並創建聚集列存儲索引:

alter table dbo.[FactOnlineSales] DROP CONSTRAINT PK_FactOnlineSales_SalesKey
alter table dbo.[FactStrategyPlan] DROP CONSTRAINT PK_FactStrategyPlan_StrategyPlanKey
alter table dbo.[FactSales] DROP CONSTRAINT PK_FactSales_SalesKey
alter table dbo.[FactInventory] DROP CONSTRAINT PK_FactInventory_InventoryKey
alter table dbo.[FactSalesQuota] DROP CONSTRAINT PK_FactSalesQuota_SalesQuotaKey
alter table dbo.[FactOnlineSales] DROP CONSTRAINT FK_FactOnlineSales_DimCurrency
alter table dbo.[FactOnlineSales] DROP CONSTRAINT FK_FactOnlineSales_DimCustomer
alter table dbo.[FactOnlineSales] DROP CONSTRAINT FK_FactOnlineSales_DimDate
alter table dbo.[FactOnlineSales] DROP CONSTRAINT FK_FactOnlineSales_DimProduct
alter table dbo.[FactOnlineSales] DROP CONSTRAINT FK_FactOnlineSales_DimPromotion
alter table dbo.[FactOnlineSales] DROP CONSTRAINT FK_FactOnlineSales_DimStore
alter table dbo.[FactStrategyPlan] DROP CONSTRAINT FK_FactStrategyPlan_DimAccount
alter table dbo.[FactStrategyPlan] DROP CONSTRAINT FK_FactStrategyPlan_DimCurrency
alter table dbo.[FactStrategyPlan] DROP CONSTRAINT FK_FactStrategyPlan_DimDate
alter table dbo.[FactStrategyPlan] DROP CONSTRAINT FK_FactStrategyPlan_DimEntity
alter table dbo.[FactStrategyPlan] DROP CONSTRAINT FK_FactStrategyPlan_DimProductCategory
alter table dbo.[FactStrategyPlan] DROP CONSTRAINT FK_FactStrategyPlan_DimScenario
alter table dbo.[FactSales] DROP CONSTRAINT FK_FactSales_DimChannel
alter table dbo.[FactSales] DROP CONSTRAINT FK_FactSales_DimCurrency
alter table dbo.[FactSales] DROP CONSTRAINT FK_FactSales_DimDate
alter table dbo.[FactSales] DROP CONSTRAINT FK_FactSales_DimProduct
alter table dbo.[FactSales] DROP CONSTRAINT FK_FactSales_DimPromotion
alter table dbo.[FactSales] DROP CONSTRAINT FK_FactSales_DimStore
alter table dbo.[FactInventory] DROP CONSTRAINT FK_FactInventory_DimCurrency
alter table dbo.[FactInventory] DROP CONSTRAINT FK_FactInventory_DimDate
alter table dbo.[FactInventory] DROP CONSTRAINT FK_FactInventory_DimProduct
alter table dbo.[FactInventory] DROP CONSTRAINT FK_FactInventory_DimStore
alter table dbo.[FactSalesQuota] DROP CONSTRAINT FK_FactSalesQuota_DimChannel
alter table dbo.[FactSalesQuota] DROP CONSTRAINT FK_FactSalesQuota_DimCurrency
alter table dbo.[FactSalesQuota] DROP CONSTRAINT FK_FactSalesQuota_DimDate
alter table dbo.[FactSalesQuota] DROP CONSTRAINT FK_FactSalesQuota_DimProduct
alter table dbo.[FactSalesQuota] DROP CONSTRAINT FK_FactSalesQuota_DimScenario
alter table dbo.[FactSalesQuota] DROP CONSTRAINT FK_FactSalesQuota_DimStore;
GO

Create Clustered Columnstore Index CCI on dbo.FactOnlineSales;

Create Clustered Columnstore index CCI on dbo.FactSales;

Create Clustered Columnstore index CCI on dbo.FactInventory;

Create Clustered Columnstore index CCI on dbo.FactSalesQuota;

   然後在每個表中隨機刪除一些數據。我使用了checksum(newid())來實現隨機,只要數據量不少,幾乎不可能重複,也因此刪除的數據可能不一樣,不過要的就是這個效果:

use ContosoRetailDW
go
delete dbo.FactOnlineSales where  OnlineSalesKey in (select top 2525521 OnlineSalesKey from dbo.FactOnlineSales order by checksum(newid()))
delete dbo.FactSales where  SalesKey in (select top 681217 SalesKey from dbo.FactSales order by checksum(newid()))
delete dbo.FactInventory where  InventoryKey in (select top 1602619 InventoryKey from dbo.FactInventory order by checksum(newid()))
delete dbo.FactSalesQuota where  SalesQuotaKey in (select top 1493182 SalesQuotaKey from dbo.FactSalesQuota order by checksum(newid()))
go

   然後使用下面的腳本來看一下是否需要重建:

SELECT object_name(p.object_id) as TableName,
		p.partition_number as Partition,
		cast( Avg( (rg.deleted_rows * 1. / rg.total_rows) * 100 ) as Decimal(5,2)) as 'Total Fragmentation (Percentage)',
		sum (case rg.deleted_rows when rg.total_rows then 1 else 0 end ) as 'Deleted Segments Count',
		cast( (sum (case rg.deleted_rows when rg.total_rows then 1 else 0 end ) * 1. / count(*)) * 100 as Decimal(5,2)) as 'DeletedSegments (Percentage)'
	FROM sys.partitions AS p 
		INNER JOIN sys.column_store_row_groups rg
			ON p.object_id = rg.object_id 
	where rg.state = 3 -- Compressed (Ignoring: 0 - Hidden, 1 - Open, 2 - Closed, 4 - Tombstone) 
	group by p.object_id, p.partition_number
	order by object_name(p.object_id);

在這裏插入圖片描述
   可以看出4個表有不同的碎片率,同時都分佈在一個分區。我有意刪除20%的數據,所以到了這個情況,應該進行索引重建。這裏爲什麼Deleted Segments Count 爲0 呢?從官方文檔 sys.column_store_row_groups可以看到對於Delta Store,總是爲0。

   本文主要是演示如何獲取碎片信息。當數量達到一定程度,應該選擇重建。
   下一文:SQL Server 列存儲索引性能總結(12)——RESOURCE_SEMAPHORE 等待

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章