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 等待

你可能感兴趣的:(SQL,On,Linux,列存储)