转:诊断索引健全性的新工具

概览:
  • 索引碎片
  • 索引使用情况
  • 索引操作活动
下载这篇文章的代码: DyessSQLIndex2007_03.exe (151KB)

在所有 SQL Server 性能调整检查表中的主要项目之一便是调整数据库中的索引。SQL Server 查询优化器在执行查询期间是否能够正确使用索引不仅取决于是否创建了有效的索引,还
取决于索引是否健全。借助 SQL Server™ 2005 中所引入的一系列动态管理视图 (DMV) 和函数 (DMF),数据库管理员能够确定其索引的有效性以及发现存在的任何性能问题。
通过 DMV 和 DMF,您能够深入了解服务器并返回服务器状态信息,这些信息可帮助您监视服务器实例的运行状况和性能以及诊断问题。那些熟悉先前版本 SQL Server 的数据库管理员会发现这些 DMV 和 DMF 将会替代以下一系列操作:使用 DBCC 命令、执行一定的系统存储过程、查询许多系统表以及利用 SQL 事件探查器捕获事件。
提供了三个重要的函数和视图(即 sys.dm_db_index_physical_stats、sys.dm_db_index_usage_stats 和 sys.dm_db_index_operational_stats)来帮助您了解索引是否如预计一样工作。可利用它们来查看索引的 I/O 和锁定模式,并了解查询优化器使用索引的方式是否不会在数据库中导致不必要的争用问题。

索引碎片
创建 sys.dm_db_index_physical_stats DMF 是为了替换 DBCC SHOWCONTIG,并能够显示索引的碎片。但是,DBCC SHOWCONTIG 是将共享锁 (S) 放置在包含索引的表上,而 sys.dm_db_index_physical_stats 仅放置一个意图共享锁 (IS),从而在函数执行期间极大地减少了表的阻塞。
要确定使用 sys.dm_db_index_physical_stats 所查到的索引碎片,请将该函数输出结果中的三列组合在一起进行检查。通过查看 avg_fragmentation_in_percent 列中所返回的值可确定索引的逻辑碎片(堆的区碎片)。逻辑碎片是在索引的叶级别中次序混乱页面的百分比,而区碎片是在索引的叶级别中次序混乱区的百分比。由于磁头只有左右跳动才能按照顺序读取页面,因此,逻辑碎片和区碎片会因为其需要额外的 I/O 和磁头运动而影响索引的性能。您应该尽量保证逻辑碎片和区碎片均接近于零。
索引的内部碎片是页填充度的百分比。当然,您会希望索引页尽量填满,但是也需要根据插入到索引页面中的页面数量来平衡填充度,以保证页面分割的数量绝对最小。
应该检查 sys.dm_db_index_physical_stats 的 avg_page_space_used_in_percent 参数以确定索引页填充度。为了正确配置该数字以使其尽量接近 100%,请在调整索引填充因子的同时观察所出现的页面分割数量。在某一刻,页面分割的数量会开始急剧增加,这表明您设置的索引填充因子数值高于其应有的数值。调整索引的填充因子需要花费一定的时间并需要进行测试,而且必须在事先进行合理规划。(如果未向索引中随意插入,则可以将索引填充因子设置为 100,且不必担心增加的页面分割数。)
要确定 AdventureWorks.HumanResources.Employee 表中所有索引的碎片级别,您可以使用如下语句:
SELECT * 
FROM sys.dm_db_index_physical_stats (DB_ID('AdventureWorks')
,OBJECT_ID('HumanResources.Employee')
,NULL 
-- NULL to view all indexes; 
-- otherwise, input index number
,NULL -- NULL to view all partitions of an index
,'DETAILED') -- We want all information
利用此 DMF,能够自动确定出哪些索引需要重建、哪些需要重组以及哪些不需要维护。检查此 DMF 的 avg_page_space_used_in_percent 和 avg_fragmentation_in_percent 列的值可得出超出所认可的逻辑和密度阈值的索引碎片,这样会有助于您确定要对索引执行哪项操作。
根据您索引的状态,图 1 中所示的示例可能不会返回 AdventureWorks 示例数据库副本中的数据,但对其经过简单调整即可适用于其他数据库。
--Reorganize the following indexes in the AdventureWorks database
USE AdventureWorks
GO

SELECT OBJECT_NAME([object_id]) AS 'Table Name',
index_id AS 'Index ID'
FROM sys.dm_db_index_physical_stats (DB_ID('AdventureWorks')
,NULL -- NULL to view all tables
,NULL -- NULL to view all indexes; otherwise, input index number
,NULL -- NULL to view all partitions of an index
,'DETAILED') --We want all information
WHERE ((avg_fragmentation_in_percent > 10 
AND avg_fragmentation_in_percent < 15) -- Logical fragmentation
OR (avg_page_space_used_in_percent < 75 
AND avg_page_space_used_in_percent > 60)) --Page density
AND page_count > 8 -- We do not want indexes less than 1 extent in size
AND index_id NOT IN (0) --Only clustered and nonclustered indexes

--Rebuild the following indexes in the AdventureWorks database
USE AdventureWorks
GO

SELECT OBJECT_NAME([object_id]) AS 'Table Name',
index_id AS 'Index ID'
FROM sys.dm_db_index_physical_stats (DB_ID('AdventureWorks')
,NULL -- NULL to view all tables
,NULL -- NULL to view all indexes; otherwise, input index number
,NULL -- NULL to view all partitions of an index
,'DETAILED') --We want all information
WHERE ((avg_fragmentation_in_percent > 15) -- Logical fragmentation
OR (avg_page_space_used_in_percent < 60)) --Page density
AND page_count > 8 -- We do not want indexes less than 1 extent in size
AND index_id NOT IN (0) --Only clustered and nonclustered indexes

将查询结果存储到某个表变量中,然后遍历该表变量,为正确的 ALTER INDEX 语句生成一个动态字符串,这是很容易实现的(请参见图 2)。
--Rebuild the following indexes in the AdventureWorks database
USE AdventureWorks
GO

--Table to hold results
DECLARE @tablevar TABLE(lngid INT IDENTITY(1,1), objectid INT,
index_id INT)

INSERT INTO @tablevar (objectid, index_id)
SELECT [object_id],index_id
FROM sys.dm_db_index_physical_stats (DB_ID('AdventureWorks')
,NULL -- NULL to view all tables
,NULL -- NULL to view all indexes; otherwise, input index number
,NULL -- NULL to view all partitions of an index
,'DETAILED') --We want all information
WHERE ((avg_fragmentation_in_percent > 15) -- Logical fragmentation
OR (avg_page_space_used_in_percent < 60)) --Page density
AND page_count > 8 -- We do not want indexes less than 1 extent in size
AND index_id NOT IN (0) --Only clustered and nonclustered indexes

SELECT 'ALTER INDEX ' + ind.[name] + ' ON ' + sc.[name] + '.'
+ OBJECT_NAME(objectid) + ' REBUILD' 
FROM @tablevar tv
INNER JOIN sys.indexes ind
ON tv.objectid = ind.[object_id]
AND tv.index_id = ind.index_id
INNER JOIN sys.objects ob
ON tv.objectid = ob.[object_id]
INNER JOIN sys.schemas sc
ON sc.schema_id = ob.schema_id


索引使用情况
尽管替换了 DBCC SHOWCONTIG 的 sys.dm_db_index_physical_stats 功能强大,并有助于显示索引是否健全,但您还会经常遇到更为复杂的问题,如确定哪些索引可用于针对表而执行的查询。通常,数据库开发人员或管理员会针对他们认为在查询执行期间查询优化器会用到的表来生成索引。在 SQL Server 的先前版本中,很难知道这些索引是否确实在使用中。用户只有删除相应索引然后查看查询性能是否受到影响,或者是捕获查询的执行计划然后扫描索引的使用情况。
现在有了一个新的动态管理视图 - sys.dm_db_index_usage_stats,这样,您很容易就可以了解到查询优化器使用索引的情况及针对表格执行查询的情况。您可以通过检查此视图来确定索引的有用性,这样您就可以删除查询优化器未在使用的任何索引。您不必再担心索引是否只是在浪费存储空间,或者对无用索引的维护会降低数据库性能。
通过检查此 DMV 的输出结果可得出没有经过搜索和扫描的索引,这样就可确定自上次启动 SQL Server 以来某索引是否曾经使用过。不过,请记住,许多 DMV 和 DMF 不是永久有效,一旦 SQL Server 重启,它们就会将自身重置为零。在使用 DMV 或 DMF 确定索引的使用情况时,请将这一点考虑进去。某索引可能会自上次重新启动服务以来一直没有使用过,但在周末、月末或季度报表查询时却需要该索引。
要查看自上次 SQL Server 服务重启以来某实例中一直未曾使用的全部索引,可以使用以下语句:
SELECT DB_NAME(database_id),OBJECT_NAME([object_id])
FROM sys.dm_db_index_usage_stats
WHERE user_seeks = 0
AND user_scans = 0
AND user_lookups = 0
AND system_seeks = 0
AND system_scans = 0
AND system_lookups = 0

索引操作活动
如果希望了解索引的操作活动,sys.dm_db_index_operational_stats DMF 会对您很有帮助。您可以使用它来查看数据库中每个索引的 I/O、锁定、闭锁和访问方法活动,它们能够帮助您了解索引的使用情况以及诊断因过多的 I/O 活动或索引中存在“热点”而引起的索引锁定问题。
使用此 DMF 的闭锁等待列可帮助确定 READ 和 WRITE 操作为获取对某索引资源的访问权限而花费的时间量。这可帮助您确定用于存储索引的磁盘子系统是否足以应付索引的 I/O 活动。它还可以指出索引的设计和使用情况是否引发了热点;热点是由于在索引的一个或多个页面中活动频繁从而导致争用这些页面中所包含的数据。这样的争用经常会导致对该区域尝试进行的 READ 或 WRITE 操作的大量阻塞。
图 3 显示了如何确定 AdventureWorks.HumanResources.Employee 表中所有索引的锁定和 I/O 模式。
SELECT page_latch_wait_count --page latch counts
,page_latch_wait_in_ms --page latch wait times
,row_lock_wait_in_ms --row lock wait times
,page_lock_wait_in_ms --page lock wait times
,row_lock_count --row lock counts
,page_lock_count --page lock counts
,page_io_latch_wait_count --I/O wait counts
,page_io_latch_wait_in_ms --I/O wait times
FROM sys.dm_db_index_operational_stats (DB_ID('AdventureWorks')
,OBJECT_ID('HumanResources.Employee')
,NULL -- NULL to view all indexes; otherwise, input index number
,NULL -- NULL to view all partitions of an index
)


详细信息
本文所介绍的 DMV 和 DMF 除了具有此处所描述的那些功能外,还有许多其他用途。在“SQL Server 联机丛书”文章中介绍了“其他资源”边栏中所指出的那些函数和视图,请花点时间回顾一下,以了解可以使用它们获得和查阅的更多信息。
要了解本文中未作讨论的一些其他索引 DMF 和 DMV,请参阅由 SQL Server 查询优化团队发布的博客文章,网址为 blogs.msdn.com/queryoptteam/570176.aspx

posted on 2011-08-17 23:19  杨剑  阅读(206)  评论(0编辑  收藏  举报

导航