2009-09-21 17 views
9

Chciałbym monitorować wykorzystanie indeksu dla bazy danych SQL, w celu znalezienia nieużywanych indeksów, a następnie upuść je. Jak najskuteczniej monitorować użycie indeksu? A które skrypty mogą być przydatne?Jak monitorować i znaleźć nieużywane indeksy w bazie danych SQL

(Jestem świadomy this question about identifying unused objects, ale dotyczy to tylko aktualnego przebiegu serwera SQL. Chciałbym monitorować użycie indeksu w okresie czasu ...)

Odpowiedz

7

Obecnie (stan na SQL Server 2005 - 2008) informacje o statystykach indeksu SQL są przechowywane tylko w pamięci, więc jeśli chcesz, aby to się utrzymywało w przypadku restartów i odłączania bazy danych, musisz wykonać niektóre prace.

Zazwyczaj robię zadanie, które działa codziennie i robi migawkę informacji znajdujących się w tabeli sys.dm_db_index_usage_stats, w niestandardowej tabeli, którą utworzę dla danej bazy danych.

Wydaje się to działać całkiem dobrze, aż do przyszłej wersji SQL, która będzie obsługiwać trwałe statystyki użycia indeksu.

3

Wyciągnął tego szczeniaka z http://blog.sqlauthority.com/2008/02/11/sql-server-2005-find-unused-indexes-of-current-database/. Zwróć uwagę, że działa to w przypadku wersji 2005 i nowszych. Kluczem jest tablica systemowa JOIN do .

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

To interesujące pytanie. W ciągu ostatniego tygodnia pracowałem nad tym samym pytaniem. Istnieje tabela systemowa o nazwie dm_db_index_usage_stats, która zawiera statystyki użycia indeksów.

indeksy, które nie pojawiają się w statystykach wykorzystania tabeli

Jednak wiele indeksy nigdy nie pojawiają się w tej tabeli w ogóle. Zapytanie, które napisał David Andres, zawiera listę wszystkich indeksów dla tego przypadku. Zaktualizowałem go nieco, aby zignorować klucze podstawowe, które prawdopodobnie nie powinny zostać usunięte, nawet jeśli nie są nigdy używane. Dołączyłem również do tabeli dm_db_index_physical_stats, aby uzyskać inne informacje, w tym liczbę stron, całkowity rozmiar indeksu i procent fragmentacji. Interesujące jest to, że indeksy zwracane przez to zapytanie nie pojawiają się w raporcie SQL dla statystyk użycia indeksu.

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 

indeksy, które są widoczne w statystykach wykorzystania tabeli, ale nigdy nie zostaną Używane

Istnieją inne wskaźniki, które pojawiają się na stole dm_db_index_usage_stats, które jednak nigdy nie zostały wykorzystane do użytkownik chce, skany lub wyszukiwania. To zapytanie zidentyfikuje indeksy należące do tej kategorii. Nawiasem mówiąc, w przeciwieństwie do indeksów zwróconych z innego zapytania, indeksy zwrócone w tym zapytaniu mogą być zweryfikowane w raporcie SQL według statystyk użycia indeksu.

Dodałem minimalną liczbę stron, która pozwala mi początkowo skupić się na i usunąć nieużywane indeksy, które zajmują dużo miejsca.

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 

Mam nadzieję, że to pomoże.

ostatnia myśl

Oczywiście, gdy indeksy są identyfikowane jako kandydatów do usuwania, rozwagą powinny być nadal stosowane, aby upewnić się, że to dobra decyzja, aby to zrobić.

Aby uzyskać więcej informacji, zobacz Identifying Unused Indexes in a SQL Server Database

3

I manipulowane zapytania Johna Pasquet tu: Identifying Unused Indexes in a SQL Server Database wrócić indeksy używane 10 lub mniej razy, unioned wyniki, które nie są w używaniu statystyki stoły, wykluczenia indeksy heap unikalne ograniczenia lub podstawowe indeksy kluczy, a na końcu wyklucza indeksy z zerowymi stronami.

Bądź ostrożny z wynikami tego zapytania - najlepiej używać go w produkcji, w której indeksy są używane w sposób zgodny z oczekiwaniami. Jeśli zapytanie dotyczy bazy danych z odbudowanymi lub usuniętymi/ponownie utworzonymi indeksami lub z ostatniej kopii zapasowej bazy danych, można uzyskać fałszywe alarmy (indeksy, które normalnie zostałyby użyte, ale nie wynikają z wyjątkowych okoliczności). Nie jest bezpieczne w środowiskach testowych i dev, aby zdecydować, czy usunąć indeksy. Jak mówi Narnian, to zapytanie właśnie identyfikuje kandydatów do usunięcia w celu dokładnego rozważenia.

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

Należy przyjrzeć Brent Ozars sp_BlitzIndex. Ta procedura przechowywana zawiera między innymi nieprzechowywane indeksy. Zawiera listę zaburzeń w raporcie. Dla każdego wpisu podany jest adres URL, który wyjaśnia, czego szukać i jak sobie z tym poradzić.

Powiązane problemy