zoukankan      html  css  js  c++  java
  • 使用SQL Server DMV调整索引策略

    【文章转自:https://www.red-gate.com/simple-talk/sql/performance/tune-your-indexing-strategy-with-sql-server-dmvs/

    使用SQL Server DMV调整索引策略

    SQL Server索引必须是有效的。太少或太多是错误的。您创建的数据库必须确保工作负载以最少的I / O快速读取数据。除了对关系数据库的工作方式有充分的了解外,它还有助于熟悉那里的动态管理对象,以帮助您建立索引策略。

    定义有效的索引策略是确保工作负载中最重要,最频繁的查询能够以逻辑有序的方式仅读取所需数据的唯一方法,从而以最少的I / O快速高效地返回该数据。哦 但是,在过多和过少的索引之间找到正确的平衡,并拥有适当的一组索引是一门精妙的技术。它需要对数据库设计,表中的数据如何分布以及典型的查询模式有全面的了解。

    这就是为什么与索引相关的动态管理对象(DMO)集可能是所有类别中使用最广泛的。所有索引DMO的名称都以sys.dm_db_开头,它们可以帮助DBA回答以下问题(一些相关的DMO在括号中指出)。

    • 是否有不再使用或从未使用过的索引?(index_usage_stats)
    • 对于正在使用的索引,使用模式是什么?(index_operational_stats)
    • 缺少哪些索引?(missing_index_details,missing_index_group_stats)

    在本文中,我们将通过示例描述如何使用DMO回答这些问题。

    索引策略的注意事项

    精心设计的SQL代码将“接触”尽可能少的基表中的数据,仅返回严格满足要求的集合数据,然后将使用基于集合的高效逻辑将这些数据处理为所需的结果集。但是,无论我们如何巧妙地设计SQL,除非我们也明智地使用了索引,否则它仍然会读取比必要数量更多的数据,并且性能很差。即使我们的查询努力地在100,000行表中从三列中仅返回了所需的20行,它仍然可能导致查询引擎扫描整个表(或聚集索引),仅检索我们需要的20行。

    虽然索引可以并且将提高查询解析的效率和性能,但错误的索引可能与正确的索引一样好。如果未使用索引,它仍然会导致任何写操作的开销:每次我们在基础表或视图中插入更新或删除记录时,引擎都必须写入索引。这种活动会导致碎片化,随着时间的流逝,DBA必须通过索引重建或重组来做出正确的反应。所有这些操作都消耗重要的系统资源。这就是为什么走在索引太少和索引太少之间的细线很重要的原因。同时,我们还需要考虑混合因素,即我们创建的索引对于遇到的负载是正确的。

    不幸的是,每个DBA都遇到了很多情况,要么完全不存在索引,要么完全混乱。就个人而言,我曾多次遇到以下情况:

    • 每列都单独索引
    • 同一列参与三个或更多组合键,并且也分别进行索引
    • 没有定义主键,但是几乎所有列上都定义了非聚集索引。

    简而言之,这是一团糟,但我们不能只是跳入并删除不需要“直觉”告诉我们的索引。在SQL Server 2005和更高版本中,通过本文介绍的索引DMO,我们DBA现在可以正确了解所使用的索引以及优化程序忽略的索引。这从清理不正确,未使用和彻头彻尾的无知索引的过程中消除了“胆量感觉”因素。

    但是,在开始检查可用于发现此信息的脚本之前,有必要先声明一下,盲目地遵循这些DMO提供的建议也不是正确的方法。如前所述,定义有效的索引策略是一项微妙的工作,通常需要您对数据库设计,表内数据的分布方式以及如何查询这些数据有充分的了解。提供有关如何确定有效索引集的完整教程超出了本文的讨论范围,但是涵盖了一些我们不希望看到的内容,因此只需对其中的一些内容进行简要介绍即可。我们喜欢。

    聚集索引和PK

    SQL Server中的每个表都应或多或少地具有聚集索引,以允许高效地搜索该表中的数据。您可以在自然反映数据查询方式的键上聚类,也可以在一个狭窄且不断增加的整数键上聚类(以最大程度地减少后续碎片),并使用非聚簇索引来提高查询效率。那里的大多数建议都指向后一种方法,但是无论如何,大多数表都应具有聚簇索引,而每个表都应具有主键(可以是也可以不是聚簇索引)。

    覆盖指标

    覆盖索引是包含查询所需的所有列和数据的索引。这意味着索引中包括在联接或搜索条件中使用的所有列,以及简单选择的所有列。后者应作为INCLUDE列而不是实际索引的一部分包括在内。如果索引涵盖了查询,则意味着优化器可以完全从索引返回数据,而无需执行可怕的表扫描或“键查找”来从聚簇索引中获取所有未覆盖的数据。这样会减少读取次数,通常是返回数据的最快,最有效的方法。之所以具有“通常”的限定条件,是因为即使存在您认为应该使用查询的索引,也无法保证优化器会选择使用它。

    小号electivity

    如果为索引键选择了低选择性列(即每个键值与许多行匹配的位置),那么优化器可能会决定执行表扫描以返回一条数据。表扫描的声誉很差,但这是因为它通常意味着要读取大量的行。在小型表中,扫描所有行有时比从索引的叶级读取数据更快。

    您正在寻找组成您的索引键的选择性列,当然,前导(第一)列应该是选择性的。但是,这并不意味着每个索引都应以PK列开头;它必须是可能会被搜索到的列。您可以在这里找到有关索引选择性和列顺序的很好的讨论:http : //sqlserverpedia.com/wiki/Index_Selectivity_and_Column_Order

    既不太多也不少

    覆盖查询的动力并不意味着您应该简单地为工作负载中的每个查询创建索引。如果一个表包含许多行并且要进行频繁的修改,那么大量索引的存在将大大减慢这些修改的速度,因为SQL Server必须同时维护索引和基础表中的数据。

    检查给定表的工作负载特征,因为这将影响每个表可能要拥有多少索引。如果一个表是非常静态的,则可以使用更多的索引,但是对于频繁更新的表,则需要尽可能少的索引。

    缩小ndexes (合理范围内)

    覆盖查询的动机也不意味着您应该创建巨大的16列索引以试图“一次发现所有内容”;如果您的索引键值很宽,那么您在页面上的容纳量将很小,索引将占用大量空间,而对其进行扫描将效率很低。搜索窄索引键要快得多。

    同样,这是一种平衡的行为。具有大量的单列索引也是一个坏主意。您的目标是使索引尽可能窄,同时可以被尽可能多的查询使用。例如,如果用户搜索员工的姓氏,那么LastName列上的索引可能是一个好主意。如果用户有时还用名字来限定搜索,则在(LastName,Firstname)上创建一个索引,因为这将满足两个查询。

    使用索引DMO

    除其他外,Microsoft SQL Server中的查询优化器还缓存与使用现有索引有关的元数据。每当SQL Server服务重新启动时,该信息都会丢失,因此我们建议您将其存储在持久表中,以维护丰富而深入的数据集,以进行性能调整。否则,我们将在本文的其余部分中创建的查询将迫使您使用可能仅几天或几小时的数据来决定实例的长期结构。我们绝不主张在如此薄弱的数据下做出这样的决定。当这些查询返回数据以引起您的注意时,我们将基于您在运行这些查询并做出您打算做出的决定时有足够的可用数据天的基础上进行操作。

    索引系统目录视图

    有时,我们将需要调查的索引和对象的详细信息,例如索引名称或索引中列的数据类型,而DMO根本无法提供这些详细信息。在这种情况下,我们需要加入系统目录视图以检索此数据。例如,我们可能使用sys.indexes返回索引标识符(例如索引名称)和配置设置(例如填充因子)或sys.columns(sys.index_columns),以查找有关索引中特定列的详细信息,例如它们的长度,数据类型等。

    调查索引使用情况(index_usage_stats)

    在本节中,我们将使用DMV sys.dm_db_index_usage_stats以及各种系统目录视图,以获取有关如何使用索引来解析查询的统计信息。该DMV特别为每个索引提供以下列:

    • database_id-数据库的ID
    • object_id –标识索引所属的表或视图,仅在数据库级别是唯一的
    • index_id –索引ID,仅对object_id的范围唯一;index_id为0表示堆(表上没有聚集索引);index_id值1总是与表上的聚集索引相关联,而大于1的index_id值则保留给非聚集索引
    • user_seeks –在搜索操作(查找特定行)中使用索引的次数
    • user_scans –通过扫描索引的叶子页上的数据来使用索引的次数
    • user_lookups –仅适用于聚集索引,这是在“书签查找”中使用索引来获取整行的次数;这是因为非聚集索引使用聚集索引键作为指向基本行的指针
    • user_updates –由于表数据的更改而修改索引的次数。

    对于每个用户操作(user_ *列),还有一个相应的last_user_ *列(例如last_user_seek),该列记录了操作上一次发生的日期和时间。此外,这些列中的每一列都有一个system_ *和last_system_ *等效项,提供有关系统操作对索引的使用的统计信息。

    此DMV中的数据是累积性的,并在重新启动服务器或删除并重新创建索引后刷新。重建或重组索引时,甚至禁用和重建索引时,统计信息仍然有效。针对此DMV的查询将返回至少已读取或写入一次的所有索引(包括堆和聚集索引)。如果存在索引,但自创建以来或自刷新统计信息以来从未使用过,则sys.dm_db_index_usage_stats中将没有该索引行的条目。它是一个实例范围的DMV,因此将为该实例上的每个数据库返回索引,但是您几乎总是想对每个数据库进行限制,使用sys.indexes使用database_id检索该数据库的索引名(如清单1)。另请注意,DMV不会区分分区,

    清单1列出了在查询执行期间至少被使用过一次的数据库索引,其中被扫描的索引排在最前面。大量扫描可能表明需要更新给定表或索引的统计信息。但是,同样地,如果查询优化器确定表足够小以至于扫描索引而不是执行查找操作,则将导致大量扫描。因此,不应孤立地考虑此查询的输出,而应与有关索引的选择性和大小的数据结合使用(可以通过针对sys.dm_db_index_physical_stats的查询返回该数据,在本文后面介绍)。

    SELECT  OBJECT_NAME(ddius.[object_id], ddius.database_id) AS [object_name] ,
            ddius.index_id ,
            ddius.user_seeks ,
            ddius.user_scans ,
            ddius.user_lookups ,
            ddius.user_seeks + ddius.user_scans + ddius.user_lookups AS user_reads ,
            ddius.user_updates AS user_writes ,
            ddius.last_user_scan ,
            ddius.last_user_update
    FROM    sys.dm_db_index_usage_stats ddius
    WHERE   ddius.database_id > 4 -- filter out system tables
            AND OBJECTPROPERTY(ddius.OBJECT_ID, 'IsUserTable') = 1
            AND ddius.index_id > 0  -- filter out heaps 
    ORDER BY ddius.user_scans DESC

    1573-4af4eb8d-127e-44be-a7c9-163f7241a1d

    1573-c1531c47-ee64-472a-8940-e817894152a

    清单1:用于解决查询的索引的使用情况统计信息。

    您将看到,在此查询以及随后的所有查询中,我们使用以下公式来计算优化器使用索引来解析用户查询的总次数:

    [user_seeks] + [user_scans] + [user_lookups] = [user reads] 

    user_updates列本身提供由于数据修改(写)而更新索引的总次数。从性能调整的角度来看,此DMV十分有价值,因为它准确显示了索引的使用方式,并且,很重要的是,它告诉我们SQL Server的先前版本没有执行的操作:哪些索引没有被使用,或者更重要的是,没有使用使用但经常更新。可以使用类似的计算来获取索引的总系统读取数。但是,从现在开始,我们将忽略任何系统活动,因为与用户驱动的活动相比,它几乎总是可以忽略的。

    在接下来的几节中,我们将向您展示脚本:

    • 查找系统上从未读取或写入的索引
    • 查找从未读取但正在维护的索引(即响应于基础表数据的修改而更新)
    • 获取所有索引的详细读/写统计信息,寻找那些维护负担可能超过其在提高查询性能方面的有用性的索引。

    这些索引是经过彻底调查后可以删除的候选对象您永远不要盲目地删除索引,并且必须确保在删除索引之前,确实没有使用索引(例如,通过不频繁但很关键的月度或季度报告查询)。

    识别从未访问过的索引

    清单2使用sys.indexes和sys.objects查找当前数据库中未在sys.dm_db_index_usage_stats中显示的表和索引。这意味着自上次启动SQL Server以来或自关闭或分离当前数据库以来(以时间较短者为准),这些索引没有读取或写入操作。

    -- List unused indexes
    SELECT  OBJECT_NAME(i.[object_id]) AS [Table Name] ,
            i.name
    FROM    sys.indexes AS i
            INNER JOIN sys.objects AS o ON i.[object_id] = o.[object_id]
    WHERE   i.index_id NOT IN ( SELECT  ddius.index_id
                                FROM    sys.dm_db_index_usage_stats AS ddius
                                WHERE   ddius.[object_id] = i.[object_id]
                                        AND i.index_id = ddius.index_id
                                        AND database_id = DB_ID() )
            AND o.[type] = 'U'
    ORDER BY OBJECT_NAME(i.[object_id]) ASC ; 

    清单2:查找未使用的索引。

    如果SQL Server的运行时间足够长,以具有完整的代表性工作负载,则很有可能这些索引(甚至表)是“失效的”,这意味着数据库不再使用它们,并且有可能将其删除,经过进一步调查。

    识别正在维护但未使用的索引

    清单3标识了聚集和非聚集索引,这些聚集和非聚集索引在写入和维护方面正在消耗资源,但从未被优化器选择使用,因此从未被读取,至少从上次清除高速缓存中的累积索引以来就从未读取过。使用数据。它使用标准的命名约定,并被标识为“ statement”,以便与查询缺失的索引DMO时看到的输出一致。我们通过联接到object_id和index_id列上的sys.indexes系统目录视图来标识索引的名称,并联接到同一列上的sys.partitions系统视图以返回total_rows指标(总数索引中的行数)。

    SELECT  '[' + DB_NAME() + '].[' + su.[name] + '].[' + o.[name] + ']' AS [statement] ,
            i.[name] AS [index_name] ,
            ddius.[user_seeks] + ddius.[user_scans] + ddius.[user_lookups] AS [user_reads] ,
            ddius.[user_updates] AS [user_writes] ,
            SUM(SP.rows) AS [total_rows]
    FROM    sys.dm_db_index_usage_stats ddius
            INNER JOIN sys.indexes i ON ddius.[object_id] = i.[object_id]
                                        AND i.[index_id] = ddius.[index_id]
            INNER JOIN sys.partitions SP ON ddius.[object_id] = SP.[object_id]
                                            AND SP.[index_id] = ddius.[index_id]
            INNER JOIN sys.objects o ON ddius.[object_id] = o.[object_id]
            INNER JOIN sys.sysusers su ON o.[schema_id] = su.[uid]
    WHERE   ddius.[database_id] = DB_ID() -- current database only
            AND OBJECTPROPERTY(ddius.[object_id], 'IsUserTable') = 1
            AND ddius.[index_id] > 0
    GROUP BY su.[name] ,
            o.[name] ,
            i.[name] ,
            ddius.[user_seeks] + ddius.[user_scans] + ddius.[user_lookups] ,
            ddius.[user_updates]
    HAVING  ddius.[user_seeks] + ddius.[user_scans] + ddius.[user_lookups] = 0
    ORDER BY ddius.[user_updates] DESC ,
            su.[name] ,
            o.[name] ,
            i.[name]; 

    1573-7a4f734d-f096-44fa-8ab8-83634c0ece5

    清单3:查询sys.dm_db_index_usage_stats以获取正在维护但未使用的索引。

    我最近在生产环境中针对由第三方提供和管理的数据库运行了该查询;我知道我会看到一些可怕的东西,但是当它返回120多个未读取的索引时,我感到很惊讶。在列出这些高写/零读索引的同时,只需在SELECT子句的末尾插入以下内容,就可以生成删除它们的命令:

            'DROP INDEX [' + i.[name] + '] ON [' + su.[name] + '].[' + o.[name]
            + '] WITH ( ONLINE = OFF )' AS [drop_command]

    确认需要从数据库中删除索引之后,只需将结果集中的DROP INDEX命令文本复制到新的查询窗口中并执行它即可。与往常一样,我们提倡先在开发环境中测试此类流程,然后再针对生产数据库运行。此外,建议您在运行该命令之前先备份数据库。

    如前所述,我不鼓励读者在没有适当调查的情况下大肆丢弃大量索引。首先,建议您通过查询sys.sysdatabases来检查最近一次清除使用情况统计信息,如清单4所示。

    清单4:索引使用情况统计信息的年龄是多少?

    同样,由于其功能本质上是周期性的(也许仅在月末过程中使用),或者仅仅是因为它是最近实现的索引,所以最近可能尚未使用索引。同样,重要的是不要删除或创建索引,除非先在非生产环境中执行适当的测试。

    识别低效的索引

    我们最终的sys.dm_db_index_usage_stats查询根据当前数据库进行过滤,并且仅包含非聚集索引。它可以帮助您确定维护特定索引的成本是否超过安装该索引所带来的收益。

    清单5:查找很少使用的索引。

    确保SQL Server实例已经运行了足够长的时间,以确保完整的典型工作负载将在报告的统计信息中表示出来。同样,不要忘记定期的报告工作负载可能不会出现在日常工作负载中。即使不经常使用促进此类工作负载的索引,但它们的存在也很关键。

    确定当前索引的使用模式(index_operational_stats)

    sys.dm_db_index_operational_stats是DMF;它接受database_id,object_id,index_id和partition_number作为参数,以标识所涉及的对象(堆索引,聚簇索引或非聚簇索引),并针对该对象的每个分区返回详细的“操作统计信息”。它提供的索引使用情况统计信息比sys.dm_db_index_usage_stats DMV提供的统计信息更为详细,并提供对象上潜在的锁或闩锁争用或对象发出过多I / O的证据。如果要返回所有行,则所有参数都可以为NULL或DEFAULT,在这种情况下,DMF将为每个数据库中的每个分区返回一行。

    此DMV中的数据是累积性的,并在重新启动服务器或删除并重新创建索引后刷新。重建,重新组织索引,甚至禁用和重建索引时,统计信息仍然有效。

    只要使用索引,索引就会始终出现在index_usage_stats DMV中,但index_operational_stats DMF返回的数据实际上稍微更“瞬变”。如(在线丛书)中所述:

    sys.dm_db_index_operational_stats返回的数据仅在表示堆或索引的元数据缓存对象可用的情况下存在...活动堆或索引很可能始终将其元数据保存在缓存中,并且累积计数可能反映了自实例以来的活动SQL Server的上次启动。不太活跃的堆或索引的元数据将在使用时移入和移出缓存。结果,它可能有也可能没有可用的值…

    由于函数的“粒度”是分区级别,因此被分为五部分的表在此DMF中将具有五行,而sys.dm_db_index_usage_stats将对象视为仅一行。如果您要统计每个使用情况,请使用使用情况统计信息,因为每个使用情况都计算一次。运营统计对象可以为记录的每种活动类型设置多个值。最后,请注意,我们不能对此DMF使用APPLY运算符。

    使用情况统计信息可让您了解优化程序如何使用索引来满足某些查询的需求,而操作统计信息则通过诸如leaf_insert_count,leaf_update_count和column等列提供有关如何在物理级别使用索引的更详细的信息。 leaf_delete_count(叶子级插入,更新和删除的累积数量),以及等效于nonleaf_ *的叶子级以上的修改。

    对于诊断对象上的资源争用,以下各列特别有用:

    • row_lock_count –已针对该索引请求的行锁数
    • row_lock_wait_count –会话针对该索引等待行锁的次数
    • row_lock_wait_in_ms –会话必须等待针对此索引的行锁的时间
    • page_lock_countpage_lock_wait_countpage_lock_wait_in_ms –与页面粒度上的row_lock值相同
    • index_lock_promotion_attempt_countindex_lock_promotion_count –尝试或允许使用该索引的操作的锁定粒度升级的次数(例如从一行到另一页)
    • page_latch_wait_countpage_latch_wait_in_ms –在对象的物理页面上要删除闩锁的等待次数和等待时间
    • page_io_latch_wait_countpage_io_latch_wait_in_ms – SQL将页面从磁盘加载到内存以进行索引操作时的等待次数和时间。

    该DMF提供了更多列,例如用于调查行溢出数据,LOB数据等的使用。有关完整列表,请参阅联机丛书。让我们看看这个DMF的作用。

    未用于用户读取的索引的详细活动信息

    清单6中的脚本仅根据sys.dm_db_index_usage_stats隔离了那些未用于用户读取的索引,然后使用sys的leaf _ * _ count和nonleaf _ * _ count列提供了有关仍在发生的写入类型的详细信息。 .dm_db_index_operational_stats。通过这种方式,您可以深入了解索引的使用方式,以及索引的确切成本。

    1573-129e5c94-db83-40e6-a443-d6b4519d9fe

    1573-1db3143e-b4aa-4077-940d-c652d4880bc

    清单6:未使用索引的详细写入信息。

    在检查输出时,很明显,即使用户在读取方面没有从它们的存在中受益,但其中一些索引仍然受到插入的冲击。如果我在现实世界中遇到了这样的元数据(眨眼,眨眼),则可以确定我会为此做些事情。

    确定行级别的锁定和阻塞

    我们还可以从sys.dm_db_index_operational_stats返回有关锁定,闩锁和阻止的信息。清单7返回与活动数据库的索引在行级别上的锁定和阻塞有关的记录。

    1573-013c5640-1d3b-430b-854c-7973bdd00c9

    清单7:检索每个索引的锁定和阻塞详细信息。

    请注意,在[%_times_blocked]和avg_row_lock_wait_in_ms列的计算中,我们必须使用十进制乘法因子:

    这是由于T-SQL中的数据类型转换过程中的一个不幸故障,您在它潜伏到您面前之前从未意识到它,并且您花了数小时试图弄清楚为什么结果不遵循基本的数学规则。除非数学公式包含小数,浮点数或其他非整数数字数据类型,否则即使数学需要保证非整数结果,结果也只会产生整数结果。您可以自己尝试。在查询窗口中执行以下代码时,您会得到什么?

    我敢打赌,答案不是1.5。解决此问题的方法是通过以十进制形式包括最适合您的公式的常数来强制转换为十进制形式,如先前的计算所示。

    识别闩锁等待

    清单8使用page_io_latch_wait_count和page_io_wait_in_ms列突出显示了我们的哪些索引遇到闩锁争用。

    1573-b576c71d-8472-4c5a-9b06-177e7b359b1

    清单8:调查闩锁等待。

    引擎读取物理页面时会发生闩锁。这样做后,它发出一个闩锁,扫描页面,读取行,然后在重要的是另一个进程需要该页面的时候释放闩锁。此过程称为延迟锁存尽管闩锁是一个良性过程,但是很方便获得此查询提供的此类信息。它使我们能够确定尝试发出闩锁时哪些索引正在等待大量等待,因为已经发出了另一个闩锁。I / O闩锁发生在磁盘到内存的传输中,并且高的I / O闩锁计数可能反映了磁盘子系统问题,尤其是当您看到平均闩锁等待时间超过15毫秒时。

    识别锁升级

    SQL Server可能会尝试升级锁,以响应减少持有的锁总数以及减少持有和管理锁所需的内存的需求。例如,单个行锁可以升级为单个表锁,或者页面锁可以升级为表锁。尽管这将降低SQL Server的开销,但缺点是并发性较低。如果正在服务器上运行的进程导致锁升级,则值得调查升级是否合理,或者是否可以执行SQL调整来防止升级。

    可以查询sys.dm_db_index_operational_stats DMV,以返回有关SQL Server将行和页锁升级为特定对象的表锁的尝试次数的信息。清单9中的查询提供有关进行这些升级尝试的频率以及执行升级的成功百分比的信息。

    1573-8908da70-6a18-4bde-8cf0-2556cd3e2e5

    清单9:调查锁升级。

    识别与锁争用关联的索引

    sys.dm_os_wait_stats DMV是一个很好的“第一手资源”,用于深入研究可能引发凌晨3点的“嘿,数据库运行缓慢”电话的问题,如果您查询sys.dm_os_wait_stats的结果指向锁定问题,清单10中的查询是调查的很好的下一步。最初的想法来自Microsoft“ SQL Server Premier现场工程师”博客,网址http://blogs.msdn.com/b/sql_pfe_blog/,并进行了一些增强,可以根据结果中的名称来标识索引。

    SELECT  OBJECT_NAME(ddios.OBJECT_ID, ddios.database_id) AS OBJECT_NAME ,
            i.name AS index_name ,
            ddios.index_id ,
            ddios.partition_number ,
            ddios.page_lock_wait_count ,
            ddios.page_lock_wait_in_ms ,
            CASE WHEN DDMID.database_id IS NULL THEN 'N'
                 ELSE 'Y'
            END AS missing_index_identified
    FROM    sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddios
            INNER JOIN sys.indexes i ON ddios.OBJECT_ID = i.OBJECT_ID
                                        AND ddios.index_id = i.index_id
            LEFT OUTER JOIN ( SELECT DISTINCT
                                        database_id ,
                                        OBJECT_ID
                              FROM      sys.dm_db_missing_index_details
                            ) AS DDMID ON DDMID.database_id = ddios.database_id
                                          AND DDMID.OBJECT_ID = ddios.OBJECT_ID
    WHERE   ddios.page_lock_wait_in_ms > 0
    ORDER BY ddios.page_lock_wait_count DESC ;

    1573-f57a91d0-5196-4e0a-9e1b-82e11b5c81c

    清单10:与锁争用关联的索引。

    请注意sys.dm_db_missing_index_details的非常有用的外部联接,以识别是否存在建议丢失索引的潜在建议,以解决锁定问题。当然,在实施任何新索引之前,您应该首先在测试环境中对其进行全面测试,在继续研究缺失的索引DMO时,我们将对其进行深入讨论。

    查找缺失的索引

    当查询优化器为查询生成执行计划时,它会确定满足搜索条件的最佳数据访问路径,然后检查是否有任何现有索引提供该路径(或类似的路径)。如果不存在理想索引,则优化器将选择最佳索引,或者仅进行表扫描,但是它会存储“缺失索引”的详细信息。此信息通过四个sys.dm_db_missing_index_ * DMO公开,这些DMO很少单独使用,而是作为一个整体使用。他们是:

    • sys.dm_db_missing_index_details –一个DMV,提供有关优化器可能选择使用的索引的详细信息(如果可用)
    • sys.dm_db_missing_index_columns – DMF接受index_handle参数并返回一个表,该表提供包含建议的缺失索引的列的详细信息
    • sys.dm_db_missing_index_group_stats – DMV返回与丢失索引组的度量有关的详细信息
    • sys.dm_db_missing_index_groups –一个DMV,提供特定组中缺少索引的详细信息;这是sys.dm_db_missing_index_details和sys.dm_db_missing_index_group_stats之间的中间联接表。

    拿破仑•波拿巴(Napoleon Bonaparte)表示,一个好的草图比冗长的演讲更好我保证这是我在本书中唯一引用19世纪法国独裁者的话,但是这种格言在这种情况下是很恰当的。图1通过missing_index_groups显示了missing_index_details和index_group_stats之间的多对多关系。

    1573-d106c4b2-9c52-4ac3-bc11-e553b74ae06

    图1:通过missing_index_groups在missing_index_details和index_group_stats之间的多对多关系。

    首先要注意的是,所有丢失的索引DMO中都没有index_id。这是因为返回的结果是针对尚未创建的索引的建议,因此尚未实现。这些DMV中记录的唯一标识符是index_handle列,该列在整个SQL Server实例中都是唯一的。

    由这些DMO中的每个存储的数据在服务器重新启动时被重置。这就是为什么保留此累积数据并保持实例处于持续运行状态如此重要的原因。您需要确保在使用此数据时,所存储的统计信息能够完全代表正常的查询工作量。重新启动一项服务,您累积的历史记录(以及为该查询和其他基于DMV的查询生成有意义的结果的能力)就是历史。

    此外,存储在这些DMO中的数据也是易变的,并且基于活动查询。通过在给定的表或视图上实现单个新索引,该对象的DMO查询结果可能不再有效。

    MSDN很好地介绍了每一个DMO返回的列(http://msdn.microsoft.com/zh-cn/library/ms187974.aspx),因此在这里,我们将仅检查每个DMO的最重要的列。

    缺少索引详细信息

    sys.dm_db_missing_index_details DMV,标识我们缺少的索引,返回标识列index_handle,OBJECT_ID和database_id的,具有下列一起:

    • equal_columns –基于相等谓词的本来会有用的列
    • inequality_columns –基于不相等谓词(即“ column = value”以外的任何比较)的本来会有用的列
    • included_columns –如果包含的话,将有助于覆盖查询的列
    • 语句–由database_id和object_id标识的对象的数据库和架构限定对象名称。

    缺少索引列

    所述sys.dm_db_missing_index_columns DMF接受index_handle作为参数(从sys.dm_db_missing_index_details或sys.dm_db_missing_index_group检索),并返回包含为每个单独的列,将弥补所识别的索引的记录的表。这使工具更容易使用列来构建CREATE INDEX语句。它仅返回三列,即column_id,column_name和column_usage,后者采用EQUALITY,INEQUALITY或INCLUDE的不言自明的值。对于列对于相等和不相等谓词都有用的情况,可能会有重复的column_name值。

    重要的是要注意,此列表中的列未按照反映索引键的最佳列顺序的方式进行排序,因此您可能必须进行一些其他调整才能获得最佳结果。

    缺少索引组

    sys.dm_db_missing_index_groups DMV仅解决sys.dm_db_missing_index_details与sys.dm_db_missing_index_group_stats之间的多对多关系。它通过以下几列来标识其缺失索引组的缺失索引:

    • index_group_handle –索引组的ID,用于将行与sys.dm_db_missing_index_group_stats视图相关联
    • index_handle –索引的句柄,用于将行与sys.dm_db_missing_index_details和sys.dm_db_missing_index_columns相关联。

    当前,组中只有一个索引,但是为了将来的兼容性,您应该考虑将此对象的关键字同时包含在两列中。

    缺少索引组统计信息

    sys.dm_db_missing_index_group_stats DMV提供所有有关的SQL Server将从丢失的指数,包括如何往往会在扫描已被使用,并寻求,以及有多少编译计划可以使用它期待利益的大小详细的统计数据。

    它的标识符列是group_handle,它与sys.dm_db_missing_index_groups中的index_group_handle联接。如上所述,目前每组只有一个索引。它还返回以下统计列:

    • unique_compiles –可能已使用索引的已编译计划的数量
    • user_seeks –用户查询中可能已使用索引的查找操作数
    • user_scans –用户查询中可能已使用索引的扫描操作数
    • last_user_seek –搜索操作最后一次使用索引的时间
    • last_user_scan –扫描操作最后一次使用索引的时间
    • avg_total_user_cost –该组中的索引本可以帮助节省查询的平均成本
    • avg_user_impact –对于可以使用该索引的查询,平均查询成本将下降的估计百分比。

    对于每个用户统计信息列,都有等效的系统列,它们记录何时将索引用于系统操作(例如自动统计信息操作)。

    last_user_ *列对于帮助您评估是否确实需要添加建议的缺失索引至关重要。如果这段时间不是最近,那么它可能会帮助临时查询,而不是正常工作量的一部分,因此创建索引的好处可能是微不足道的,甚至是有害的。该表经常更新。

    缺少索引DMO的局限性

    尽管这些DMO在优化索引策略和查询执行时间方面可能非常有用,但应谨慎使用它们提供的信息。就像我们建议您在不进行彻底调查的情况下避免大幅度删除索引一样,您也绝不能盲目添加这些DMO建议的每个索引。如果您有OLTP工作负载,则尤其如此,因为其中的索引过多可能与索引太少一样有害。每次在表中更新数据时,索引中的数据也必须维护。这会大大降低这些数据修改的性能。

    相反,您需要仔细检查查询结果,并手动过滤掉不属于常规工作负载的结果。

    另外,请注意,这些DMO列出缺少的列的顺序不能准确地为索引键建议正确的列顺序。此外,根据我们的经验,这些DMO在建议INCLUDE色谱柱时通常过于热情。它们最适合用来在索引策略中找到最大的“漏洞”,而不是作为微调工具。

    此外,Microsoft联机丛书列出了以下特定限制:

    • 无法收集超过500个缺少的索引组的统计信息
    • 对于仅涉及不等式谓词的查询,返回的成本信息不那么准确
    • 报告仅包含某些查询的列,因此必须手动选择索引键列
    • 仅返回有关可能缺少索引的列的原始信息
    • 可以为在XML Showplans中多次出现的同一缺失索引组返回不同的成本。

    寻找最有利的缺失指标

    那么,如何才能充分利用这些DMO?我们的目标显然是获得缺少索引的列表,最有用的列表在顶部。Microsoft的SQL Server查询优化团队基于_group_stats DMV中的列,提出了以下公式来计算建议索引的总体收益,并且该公式已被广泛采用:

    清单11根据此公式提供了一个快速而有用的查询,DBA可以运行该查询来识别潜在有用的索引。该查询的结果在整个实例范围内,因此请确保将您的结果限制为WHERE子句中所讨论的数据库。此查询直接从查询优化器历史记录中向DBA提供信息,该历史记录是自上次重新启动SQL Server服务以来产生的。它基于执行时对查询的原始解析,提供有关优化器希望为其建立索引的列的信息。分别标识相等列,不相等列和包含列。还介绍了应计的编译次数和查找数,以及计算得出的数字,这些数字表示创建索引后要获得的改进量。

    1573-6819b629-239c-4672-af17-c492bc6a188

    1573-253c4823-ea09-4d47-8642-ffd5c9f5b52

    清单11:查找有益的缺失索引。

    该查询代表了一个强大的工具,可以帮助DBA找出提高性能的途径。但是,SQL Server查询优化团队又采取了这一步或提供了两个步骤,并提供了“缺少索引”工具,该工具可从http://blogs.msdn.com/queryoptteam/archive/2006/06/01/613516获得。 aspx,它生成有关可能有用的索引的许多详细信息。

    但是,无论哪种情况,我们都不应简单地创建结果建议的每个索引。如本文前面“索引策略”部分开头所讨论的,在表上创建过多索引可能会与未创建足够(或创建错误)索引一样对性能造成损害。我们建议您为测试和开发工作创建一个重复的环境,以表示生产环境的硬件和SQL Server配置。然后,您应该完成创建通过这些结果推荐的索引的过程,并在模拟的生产负载中测试性能。

    概括

    在本文中,我们讨论了可帮助DBA定义有效SQL Server索引策略的DMO。这是确保工作负载中最重要和最频繁查询可以以逻辑,有序的方式读取所需数据的最佳方法之一,从而避免了不必要的I / O。对于每个希望从SQL Server获得最佳性能的DBA来说,找到过多和过少的索引之间的正确平衡,并适当设置一组“有用的”索引非常重要。

    本文介绍了如何:

    • 使用sys.dm_db_index_usage_stats DMV来发现那些存在但从未使用过的索引,或者索引的维护成本很高(也许是因为表数据定期更新)的地方,但是很少将索引用作数据访问路径,因此在查询性能方面提供的收益相对较少
    • 使用sys.dm_db_index_operational_stats DMF获取聚集索引,堆或索引的“物理”使用情况统计信息,以便我们可以调查对象上潜在的锁或闩锁争用或对象发出的过多I / O,所有这些可能会导致用户等待大量时间才能从对象中读取数据
    • 使用DMO的sys.dm_db_missing_组来标识优化器在为给定查询寻找最佳数据访问路径时希望具有的索引

    在整篇文章中,我们都强调了在创建或删除由本文介绍的DMO查询标识的任何索引之前,DBA必须应用判断以及他们对数据库,其数据和正常查询工作量的了解。 。特别是,请确保SQL Server实例已经运行了足够长的时间,足以在报告的统计信息中表示完整的典型工作负荷,并且不要忘记考虑可能不会在报表中显示的定期报告作业所需的索引。

  • 相关阅读:
    如何在Eclipse中查看Java类库的源代码以及相应的api
    深入剖析ConcurrentHashMap
    Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析
    Quartz配置
    Spring 自动定时任务配置
    @Resource注解的官方解释
    @Resource 注解的使用
    扫地机器人会否抛弃激光雷达这位原配?
    女教授领军打造最耐用机器人,可从180米高空落下执行救援任务
    一文看懂80年“AI革命”简史
  • 原文地址:https://www.cnblogs.com/wz122889488/p/14505594.html
Copyright © 2011-2022 走看看