2009-09-21 17 views
9

SQLデータベースのインデックス使用状況を監視して、未使用のインデックスを見つけて削除したいとします。インデックスの使用状況を最も効率的に監視するにはどうすればよいですか?そして、どのスクリプトが役に立つのでしょうか?SQLデータベースで未使用のインデックスを監視および検索するには

は(私はthis question about identifying unused objectsの承知しているが、これは、SQL Serverの現在の実行にのみ適用されます。私は、時間の期間にわたってインデックスの使用状況を監視したいと思います...)現在

答えて

7

(SQLのようSQL Server 2005 - 2008)では、SQLインデックス統計情報はメモリ内にのみ保持されるため、再起動とデータベースのデタッチで永続化したい場合は、自分で作業する必要があります。私は通常、何

、私は毎日実行され、私は疑問にデータベースの作成したカスタムテーブルに、sys.dm_db_index_usage_statsテーブルで見つかった情報のスナップショットを取り、ジョブを作成することです。

これは、永続的なインデックス使用統計をサポートする将来のバージョンのSQLまではうまくいくようです。

3

http://blog.sqlauthority.com/2008/02/11/sql-server-2005-find-unused-indexes-of-current-database/からこの子犬を引っ張った。これは2005年以上で動作することに注意してください。キーはJOINからSYS.DM_DB_INDEX_USAGE_STATSのシステムテーブルです。

USE AdventureWorks 
GO 
DECLARE @dbid INT 
SELECT @dbid = DB_ID(DB_NAME()) 
SELECT OBJECTNAME = OBJECT_NAME(I.OBJECT_ID), 
        INDEXNAME = I.NAME, 
        I.INDEX_ID 
FROM SYS.INDEXES I 
JOIN SYS.OBJECTS O ON I.OBJECT_ID = O.OBJECT_ID 
WHERE OBJECTPROPERTY(O.OBJECT_ID,'IsUserTable') = 1 
AND I.INDEX_ID NOT IN (

SELECT S.INDEX_ID 
FROM SYS.DM_DB_INDEX_USAGE_STATS S 
WHERE S.OBJECT_ID = I.OBJECT_ID 
AND I.INDEX_ID = S.INDEX_ID 
AND DATABASE_ID = @dbid) 
ORDER BY OBJECTNAME, 
     I.INDEX_ID, 
     INDEXNAME ASC 
GO 
7

これは興味深い質問です。私は先週、この同じ質問に取り組んできました。 dm_db_index_usage_statsというシステム表があり、索引の使用統計が入っています。利用統計表

に表示され決して

インデックスはしかし、多くのインデックスは全くこのテーブルには表示されません。 David Andresが投稿したクエリでは、このケースのすべてのインデックスが一覧表示されます。主キーを無視するように少し更新しましたが、これはおそらく削除されるべきではありません。また、dm_db_index_physical_statsテーブルに加わって、ページ数、合計インデックスサイズ、断片化率などの他の情報を取得しました。興味深いことに、このクエリによって返されるインデックスは、インデックス使用統計のSQLレポートに表示されないようです。使用状況の統計表に表示されます、しかし、決して使用されない

DECLARE @dbid INT 
SELECT @dbid = DB_ID(DB_NAME()) 

SELECT Databases.Name AS [Database], 
     Objects.NAME AS [Table], 
     Indexes.NAME AS [Index], 
     Indexes.INDEX_ID, 
     PhysicalStats.page_count as [Page Count], 
     CONVERT(decimal(18,2), PhysicalStats.page_count * 8/1024.0) AS [Total Index Size (MB)], 
     CONVERT(decimal(18,2), PhysicalStats.avg_fragmentation_in_percent) AS [Fragmentation (%)] 
FROM SYS.INDEXES Indexes 
    INNER JOIN SYS.OBJECTS Objects ON Indexes.OBJECT_ID = Objects.OBJECT_ID 
    LEFT JOIN sys.dm_db_index_physical_stats(@dbid, null, null, null, null) PhysicalStats 
     on PhysicalStats.object_id = Indexes.object_id and PhysicalStats.index_id = indexes.index_id 
    INNER JOIN sys.databases Databases 
     ON Databases.database_id = PhysicalStats.database_id 
WHERE OBJECTPROPERTY(Objects.OBJECT_ID,'IsUserTable') = 1 
    AND Indexes.type = 2 -- Nonclustered indexes 
    AND Indexes.INDEX_ID NOT IN (
      SELECT UsageStats.INDEX_ID 
      FROM SYS.DM_DB_INDEX_USAGE_STATS UsageStats 
      WHERE UsageStats.OBJECT_ID = Indexes.OBJECT_ID 
       AND Indexes.INDEX_ID = UsageStats.INDEX_ID 
       AND DATABASE_ID = @dbid) 
ORDER BY PhysicalStats.page_count DESC, 
     Objects.NAME, 
     Indexes.INDEX_ID, 
     Indexes.NAME ASC 

インデックス

ユーザーが求めるために使用されたことがないdm_db_index_usage_statsテーブルに表示されますが、ない他の指標がありますが、スキャン、またはルックアップ。このクエリは、このカテゴリに属する​​インデックスを識別します。ちなみに、他のクエリから返されたインデックスとは異なり、このクエリで返されるインデックスは、インデックス使用統計によってSQLレポートで確認できます。

私は最初に多くのストレージを占有している未使用インデックスに集中して削除できる最小ページカウントを追加しました。

DECLARE @MinimumPageCount int 
SET @MinimumPageCount = 500 

SELECT Databases.name AS [Database], 
     Indexes.name AS [Index], 
     Objects.Name AS [Table],      
     PhysicalStats.page_count as [Page Count], 
     CONVERT(decimal(18,2), PhysicalStats.page_count * 8/1024.0) AS [Total Index Size (MB)], 
     CONVERT(decimal(18,2), PhysicalStats.avg_fragmentation_in_percent) AS [Fragmentation (%)], 
     ParititionStats.row_count AS [Row Count], 
     CONVERT(decimal(18,2), (PhysicalStats.page_count * 8.0 * 1024)/ParititionStats.row_count) AS [Index Size/Row (Bytes)] 
FROM sys.dm_db_index_usage_stats UsageStats 
    INNER JOIN sys.indexes Indexes 
     ON Indexes.index_id = UsageStats.index_id 
      AND Indexes.object_id = UsageStats.object_id 
    INNER JOIN sys.objects Objects 
     ON Objects.object_id = UsageStats.object_id 
    INNER JOIN SYS.databases Databases 
     ON Databases.database_id = UsageStats.database_id  
    INNER JOIN sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS PhysicalStats 
     ON PhysicalStats.index_id = UsageStats.Index_id 
      and PhysicalStats.object_id = UsageStats.object_id 
    INNER JOIN SYS.dm_db_partition_stats ParititionStats 
     ON ParititionStats.index_id = UsageStats.index_id 
      and ParititionStats.object_id = UsageStats.object_id   
WHERE UsageStats.user_scans = 0 
    AND UsageStats.user_seeks = 0 
    AND UsageStats.user_lookups = 0 
    AND PhysicalStats.page_count > @MinimumPageCount -- ignore indexes with less than 500 pages of memory 
    AND Indexes.type_desc != 'CLUSTERED'    -- Exclude primary keys, which should not be removed  
ORDER BY [Page Count] DESC 

これが役立ちます。もちろん

決勝思想

インデックスを除去するため候補として識別されると、慎重な検討はまだそれがそうするのは良い決断だことを確認するために使用されなければなりません。詳細については

Identifying Unused Indexes in a SQL Server Database

3

私はここにジョンPasquetのクエリを微調整参照:Identifying Unused Indexes in a SQL Server Databaseをインデックスを返すためには10倍以下を使用し、使用統計情報の表にない結果をUNION句、ヒープのインデックスを除外し、固有の制約または主キー索引、最後にゼロページの索引を除外します。

このクエリの結果に注意してください。インデックスが実際に期待通りに使用されている運用環境で使用することをお勧めします。再構築または削除/再作成されたインデックスを持つデータベースまたは最近のデータベースバックアップでクエリを実行すると、誤検出(通常は使用されるが特別な状況のためではないインデックス)が発生する可能性があります。テストや開発環境でインデックスを削除するかどうかを決めるのは安全ではありません。 Narnianによると、このクエリは、あなたの慎重な検討のために削除の候補者を特定するだけです。

USE [DatabaseName] 

DECLARE @MinimumPageCount int 
SET @MinimumPageCount = 500 

DECLARE @dbid INT 
SELECT @dbid = DB_ID(DB_NAME()) 

-- GET UNUSED INDEXES THAT APPEAR IN THE INDEX USAGE STATS TABLE 

SELECT 
    Databases.name AS [Database] 
    ,object_name(Indexes.object_id) AS [Table] 
    ,Indexes.name AS [Index] 
    ,PhysicalStats.page_count as [Page Count] 
    ,CONVERT(decimal(18,2), PhysicalStats.page_count * 8/1024.0) AS [Total Index Size (MB)] 
    ,CONVERT(decimal(18,2), PhysicalStats.avg_fragmentation_in_percent) AS [Fragmentation (%)] 
    ,ParititionStats.row_count AS [Row Count] 
    ,CONVERT(decimal(18,2), (PhysicalStats.page_count * 8.0 * 1024)/ParititionStats.row_count) AS [Index Size Per Row (Bytes)] 
    ,1 AS [Appears In Usage Stats Table] 

FROM sys.dm_db_index_usage_stats UsageStats 

INNER JOIN sys.indexes Indexes 
    ON Indexes.index_id = UsageStats.index_id AND Indexes.object_id = UsageStats.object_id 

INNER JOIN SYS.databases Databases 
    ON Databases.database_id = UsageStats.database_id 

INNER JOIN sys.dm_db_index_physical_stats (DB_ID(),NULL,NULL,NULL,NULL) AS PhysicalStats 
    ON PhysicalStats.index_id = UsageStats.Index_id AND PhysicalStats.object_id = UsageStats.object_id 

INNER JOIN SYS.dm_db_partition_stats ParititionStats 
    ON ParititionStats.index_id = UsageStats.index_id AND ParititionStats.object_id = UsageStats.object_id 

WHERE 
    UsageStats.user_scans <= 10 
    AND UsageStats.user_seeks <= 10 
    AND UsageStats.user_lookups <= 10 

    -- exclude heap indexes 
    AND Indexes.name IS NOT NULL 

    -- ignore indexes with less than a certain number of pages of memory 
    AND PhysicalStats.page_count > @MinimumPageCount 

    -- Exclude primary keys, which should not be removed 
    AND Indexes.is_primary_key = 0 

    -- ignore unique constraints - those shouldn't be removed 
    AND Indexes.is_unique_constraint = 0 
    AND Indexes.is_unique = 0 

UNION ALL 
(
    -- GET UNUSED INDEXES THAT DO **NOT** APPEAR IN THE INDEX USAGE STATS TABLE 

    SELECT 
     Databases.Name AS [Database] 
     ,Objects.NAME AS [Table] 
     ,Indexes.NAME AS [Index] 
     ,PhysicalStats.page_count as [Page Count] 
     ,CONVERT(decimal(18,2), PhysicalStats.page_count * 8/1024.0) AS [Total Index Size (MB)] 
     ,CONVERT(decimal(18,2), PhysicalStats.avg_fragmentation_in_percent) AS [Fragmentation (%)] 
     ,-1 AS [Row Count] 
     ,-1 AS [Index Size Per Row (Bytes)] 
     ,0 AS [Appears In Usage Stats Table] 

    FROM SYS.INDEXES Indexes 

    INNER JOIN SYS.OBJECTS Objects 
     ON Indexes.OBJECT_ID = Objects.OBJECT_ID 

    LEFT JOIN sys.dm_db_index_physical_stats(@dbid, null, null, null, null) PhysicalStats 
     ON PhysicalStats.object_id = Indexes.object_id AND PhysicalStats.index_id = indexes.index_id 

    INNER JOIN sys.databases Databases 
     ON Databases.database_id = PhysicalStats.database_id 

    WHERE 
     Objects.type = 'U' -- Is User Table 

     -- exclude heap indexes 
     AND Indexes.name IS NOT NULL 

     -- exclude empty tables 
     AND PhysicalStats.page_count <> 0 

     -- Exclude primary keys, which should not be removed 
     AND Indexes.is_primary_key = 0 

     -- ignore unique constraints - those shouldn't be removed 
     AND Indexes.is_unique_constraint = 0 
     AND Indexes.is_unique = 0 

     AND Indexes.INDEX_ID NOT IN 
     (
      SELECT UsageStats.INDEX_ID 
      FROM SYS.DM_DB_INDEX_USAGE_STATS UsageStats 
      WHERE 
       UsageStats.OBJECT_ID = Indexes.OBJECT_ID 
       AND Indexes.INDEX_ID = UsageStats.INDEX_ID 
       AND DATABASE_ID = @dbid 
     ) 
) 

ORDER BY [Table] ASC, [Total Index Size (MB)] DESC 
0

あなたはブレントOzars sp_BlitzIndexを見てみる必要があります。このストアドプロシージャには、使用されていないインデックスが一覧表示されます。それはレポートの障害をリストします。各エントリについて、検索する内容と問題の処理方法を説明するURLが表示されます。