索引维护
问:我已将索引维护例程更改为使用联机索引重新生成,但有时在维护例程运行时仍会出现阻塞问题。 为什么会这样? 我认为联机索引操作不使用锁定,所以不应该出现任何阻塞才对。 这是正常现象,还是我的操作有问题?
答:您看到的是正常现象。 在操作开始时有一个必需的共享表锁定,同时操作初始化(这个过程非常快)。 此过程转瞬即逝。 此锁定的排队方式必须与任何其他锁定类似,并且它将阻止任何新查询对该表进行修改,直到您再次同意并释放此锁定。
在您完成所有当前正在运行的修改查询后,才可以获取此锁定。 这可能需要相当长一段时间,具体取决于您的工作负荷。 这意味着在联机索引操作开始时可能会出现阻塞。
在操作结束时,必须对锁定进行架构修改,将该锁定视为独占锁定以完成修改。 此过程同样相当快。 然后立即解除独占该锁定。 此锁定将阻止对表进行任何类型的新查询(读取或写入),直到您同意并释放此锁定。
再次重申,在 SQL 完成所有当前正在运行的读取或写入查询后,才可以获取此锁定。 这同样意味着可能出现阻塞。
总而言之,尽管此功能名为联机索引操作,但它的确还需要两个可能导致阻塞问题的短期锁定。 与传统的脱机索引操作相比,这种方法的优点在于绝大多数索引操作没有使用锁定,因此整体并发程度有所提高。 “SQL Server 2005 中的联机索引操作”白皮书对这些操作的工作原理进行了更为详细的介绍。
缩短索引维护时间
问:我继承了一些系统,在这些系统中,常规索引维护操作的运行时间很长并生成大量 IO,但因为索引不能是零碎的,所以我没有重新生成任何索引。 我希望减少工作负荷,因为性能没有得到任何改进。 您能为我建议一个有帮助的策略吗?
答: 这个问题普遍存在。 原因在于维护索引操作决定要重新生成或重新组织哪些索引的方式。
大多数人针对数据库中的所有索引运行 sys.dm_db_index_physical_stats 动态管理函数(以前提到过),然后选择是重新生成、重新组织还是不执行任何操作。 他们使用输出中的 WHERE 子句根据 avg_fragmentation_in_percent、page_count 和 avg_page_space_used_in_percent 值做出决定。
问题是索引碎片不像其他统计信息一样存储在内存中。 此函数必须读取并处理各个索引才能确定其零碎程度。 如果数据库中的大多数索引一成不变或缓慢变化(就碎片来说),将不会重新生成或重新组织这些索引。 每次执行维护索引操作时检查它们的碎片根本就是在浪费时间。
大多数动态管理视图支持“谓词推送”,即只处理与 WHERE 子句中的谓词匹配的数据。 但是,sys.dm_db_index_physical_stats 是一个函数而不是视图,因此它无法做到这一点。 这就意味着您必须手动筛选函数,并要求函数只处理您知道可能成为碎片且可能需要重新生成或重新组织的那些索引。
我建议监视几周内出现的碎片。 这样您就可以了解哪些索引需要检查碎片,而不用检查所有索引。 有了这些索引的列表后,创建一个包含表名称、索引名称和碎片阈值的表以便采取措施。 您可能会发现,一些索引包含较多的碎片才会影响性能,而另一些则不然。 这将是您稍后用来推动索引维护操作的“驱动力表”。 系统将循环处理该表中介绍的所有索引,并只对它们运行 sys.dm_db_index_physical_stats 函数。
我已对多个客户应用了此方法。 在某些情况下,索引维护操作的运行时间会从几小时缩短到 15 分钟或更短。 这完全是因为没有对静态索引运行此函数。 您还可以进一步跟踪索引重新生成以及可能自动更改索引的 FILLFACTOR 设置的频率,希望这可以进一步减少索引维护操作所执行的工作。