在索引碎片里我们解释了不同类型的碎片,还有它们如何影响查询性能。在这个文章里,我们会讨论下如何检测索引碎片。
内部碎片是关于页面饱和度的一切,可以用DETAILED模式的 sys.dm_db_index_physical_stats,avg_page_space_used_in_percent 列会给出索引的内部碎片,下面的查询会列出超过10个页面,且页面饱和度低于85%的索引。
1 EXEC sp_configure 'show advanced options', 1 2 GO 3 RECONFIGURE WITH OVERRIDE 4 GO 5 DECLARE @DefaultFillFactor INT 6 DECLARE @Fillfactor TABLE 7 ( 8 Name VARCHAR(100) , 9 Minimum INT , 10 Maximum INT , 11 config_value INT , 12 run_value INT 13 ) 14 INSERT INTO @Fillfactor 15 EXEC sp_configure 'fill factor (%)' 16 SELECT @DefaultFillFactor = CASE WHEN run_value = 0 THEN 100 17 ELSE run_value 18 END 19 FROM @Fillfactor 20 21 SELECT DB_NAME() AS DBname , 22 QUOTENAME(s.name) AS CchemaName , 23 QUOTENAME(o.name) AS TableName , 24 i.name AS IndexName , 25 stats.Index_type_desc AS IndexType , 26 stats.page_count AS [PageCount] , 27 stats.partition_number AS PartitionNumber , 28 CASE WHEN i.fill_factor > 0 THEN i.fill_factor 29 ELSE @DefaultFillFactor 30 END AS [Fill Factor] , 31 stats.avg_page_space_used_in_percent , 32 CASE WHEN stats.index_level = 0 THEN 'Leaf Level' 33 ELSE 'Nonleaf Level' 34 END AS IndexLevel 35 FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') 36 AS stats , 37 sys.objects AS o , 38 sys.schemas AS s , 39 sys.indexes AS i 40 WHERE o.OBJECT_ID = stats.OBJECT_ID 41 AND s.schema_id = o.schema_id 42 AND i.OBJECT_ID = stats.OBJECT_ID 43 AND i.index_id = stats.index_id 44 AND stats.avg_page_space_used_in_percent <= 85 45 AND stats.page_count >= 10 46 AND stats.index_id > 0 47 ORDER BY stats.avg_page_space_used_in_percent ASC , 48 stats.page_count DESC
这里我在WHERE条件里指定了只列出超过10页,且页面饱和度低于85%的结果。这是基于我当前系统环境和一些文档的最佳实践。avg_page_space_used_in_percent 的低值,加上PageCount 的高值,会影响到系统性能。avg_page_space_used_in_percent 的低值会下列不同的原因:
外部检测也是用LIMITED模式的sys.dm_db_index_physical_stats,但我们使用avg_fragmentation_in_percent 的结果来检测外部碎片。使用LIMITED模式会给我们叶子层的碎片。如果要获得非页层的碎片,可以使用DETAILED或SAMPLE模式。碎片是页的连续分配。例如如果一个索引有150页,页分配从1到50,55到60,65到120,还有140到180。每个这样序列被称为碎片,这里就是有4个碎片。
1 EXEC sp_configure 'show advanced options', 1 2 GO 3 RECONFIGURE WITH OVERRIDE 4 GO 5 DECLARE @DefaultFillFactor INT 6 DECLARE @Fillfactor TABLE 7 ( 8 Name VARCHAR(100) , 9 Minimum INT , 10 Maximum INT , 11 config_value INT , 12 run_value INT 13 ) 14 INSERT INTO @Fillfactor 15 EXEC sp_configure 'fill factor (%)' 16 SELECT @DefaultFillFactor = CASE WHEN run_value = 0 THEN 100 17 ELSE run_value 18 END 19 FROM @Fillfactor 20 21 SELECT DB_NAME() AS DBname , 22 QUOTENAME(s.name) AS CchemaName , 23 QUOTENAME(o.name) AS TableName , 24 i.name AS IndexName , 25 stats.Index_type_desc AS IndexType , 26 stats.page_count AS [PageCount] , 27 stats.partition_number AS PartitionNumber , 28 CASE WHEN i.fill_factor > 0 THEN i.fill_factor 29 ELSE @DefaultFillFactor 30 END AS [Fill Factor] , 31 stats.avg_fragmentation_in_percent , 32 stats.fragment_count , 33 CASE WHEN stats.index_level = 0 THEN 'Leaf Level' 34 ELSE 'Nonleaf Level' 35 END AS IndexLevel 36 FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') 37 AS stats , 38 sys.objects AS o , 39 sys.schemas AS s , 40 sys.indexes AS i 41 WHERE o.OBJECT_ID = stats.OBJECT_ID 42 AND s.schema_id = o.schema_id 43 AND i.OBJECT_ID = stats.OBJECT_ID 44 AND i.index_id = stats.index_id 45 AND stats.avg_fragmentation_in_percent >= 20 46 AND stats.page_count >= 1000 47 ORDER BY stats.avg_fragmentation_in_percent DESC , 48 stats.page_count DESC
在这个查询里,我使用的WHERE条件只列出碎片大于20%且最少1000页的索引。avg_fragmentation_in_percent 值高的话,可能有下列原因: