2009-09-21 1 views
9

Я хотел бы отслеживать использование индекса для базы данных sql, чтобы найти неиспользуемые индексы, а затем удалить их. Как я могу лучше контролировать использование индекса? И какие скрипты могут быть полезны?Как отслеживать и находить неиспользуемые индексы в базе данных sql

(я знаю this question about identifying unused objects, но это относится только к текущему ходу сервера SQL. Я хотел бы контролировать использование индекса в течение определенного периода времени ...)

ответ

7

В настоящее время (по состоянию на 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, которая содержит статистику использования индексов.

Индексы, которые никогда не появляются в таблице статистики использования

Однако многие показатели никогда не появляются в этой таблице вообще. В запросе Дэвида Андреса были перечислены все индексы для этого случая. Я немного обновил его, чтобы игнорировать первичные ключи, которые, вероятно, не следует удалять, даже если они никогда не используются. Я также присоединился к таблице 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 или меньше раз, unioned результатов, которые не в использовании STATs таблицы, исключение индексов кучи и уникальные ограничения или индексы первичных ключей и, наконец, исключить индексы с нулевыми страницами.

Будьте осторожны с результатами этого запроса - лучше всего использовать в производстве, где индексы фактически используются так, как вы ожидали. Если вы запрашиваете базу данных с перестроенными или упакованными/воссозданными индексами или в недавней резервной копии базы данных, вы можете получить ложные срабатывания (индексы, которые обычно будут использоваться, но не из-за особых обстоятельств). Небезопасно использовать в тестовых или dev средах, чтобы решить, следует ли отказывать индексы. Как говорит Нарнян, этот запрос просто идентифицирует кандидатов для удаления для вашего тщательного рассмотрения.

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

Вы должны взглянуть на Brent Ozars sp_BlitzIndex. Эта хранимая процедура содержит среди прочего неиспользуемые индексы. В нем перечислены нарушения в отчете. Для каждой записи указывается URL-адрес, который объясняет, что искать и как справиться с проблемой.