SQL Server

[MSSQL] 7. 전체 인덱스 사용 여부 조회

푸팟퐁쿼리 2023. 4. 14. 12:05

sys.indexes DMV를 이용하여 각 DB별로 접속하여 조회하는 쿼리입니다.

아래 쿼리로 대략적으로 인덱스가 사용되고 있는지를 확인해볼 수 있습니다. 

 

만약 인덱스가 불필요로 나와 삭제하려는 경우 세션 모니터링 등을 통해 삭제하려는 인덱스가 정말로 미사용 중인지 다시 한번 체크해야합니다.

그 이유는 sys.dm_db_index_usage_stats DMV가 메모리 상에만 존재하기 때문에(저장이 안되기 때문에)

DB가 재기동된지 얼마 지나지 않았거나, 서버 메모리가 부족해 해당 DMV의 성능 정보가 Cached-Out되었으면 조회가 불가능하기 때문입니다. 

 

IF OBJECT_ID(N'tempdb..#DB') IS NOT NULL
BEGIN
    DROP TABLE #DB
END
 
CREATE TABLE #DB (name sysname, dbid smallint)
 
INSERT INTO #DB (name, dbid)
SELECT name, dbid FROM master.dbo.sysdatabases
WHERE name not in ('master', 'msdb', 'model')
GO
 
 
DECLARE @dbname sysname
DECLARE @min_dbid smallint
DECLARE @SQL varchar(8000)
 
IF OBJECT_ID(N'tempdb..#TABLEINDEX') IS NOT NULL
BEGIN
     DROP TABLE #TABLEINDEX
END
 
CREATE TABLE #TABLEINDEX (
        database_id INT
    ,   dbname sysname
    ,   SchemaName sysname
    ,   tablename sysname
    ,   indexid int
    ,   indexname varchar(500)
    ,   type_desc varchar(500)
    ,   is_primary_key INT
    ,   is_unique INT
    ,   is_unique_constraint INT
    ,   KeyColumns varchar(8000)
    ,   IncludedColumns varchar(8000)
    ,   user_seeks  BIGINT
    ,   user_scans  BIGINT
    ,   user_lookups BIGINT
    ,   user_updates BIGINT
);
 
SET @min_dbid = 4
 
WHILE (@min_dbid IS NOT NULL)
BEGIN
  SELECT @min_dbid = MIN(dbid) FROM #DB WHERE dbid > @min_dbid
  IF (@min_dbid IS NULL)  BREAK
 
  SELECT @dbname = name FROM #DB WHERE dbid = @min_dbid
 
  SET @SQL = 'USE [' + @dbname + ']; '
  SET @SQL = @SQL + '
    SELECT
        DB_ID() as database_id,
        DB_NAME() as dbName,
        (SCHEMA_NAME(t.schema_id)) AS SchemaName,
        (t.name) AS TableName,
        i.index_id,
        (i.name) AS IndexName,
        i.type_desc,
        i.is_primary_key,
        i.is_unique,
        i.is_unique_constraint AS UniqCon,
        STUFF(REPLACE(REPLACE((
            SELECT QUOTENAME(c.name) + CASE WHEN ic.is_descending_key = 1 THEN '' DESC'' ELSE '''' END AS [data()]
            FROM sys.index_columns AS ic
            INNER JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
            WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0
            ORDER BY ic.key_ordinal
            FOR XML PATH
        ), ''<row>'', '', ''), ''</row>'', ''''), 1, 2, '''') AS KeyColumns,
        STUFF(REPLACE(REPLACE((
            SELECT QUOTENAME(c.name) AS [data()]
            FROM sys.index_columns AS ic
            INNER JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
            WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 1
            ORDER BY ic.index_column_id
            FOR XML PATH
        ), ''<row>'', '', ''), ''</row>'', ''''), 1, 2, '''') AS KeyColumns,
        u.user_seeks,
        u.user_scans,
        u.user_lookups,
        u.user_updates
    FROM sys.tables AS t
    INNER JOIN sys.indexes AS i ON t.object_id = i.object_id
    LEFT JOIN sys.dm_db_index_usage_stats AS u ON i.object_id = u.object_id AND i.index_id = u.index_id
    WHERE t.is_ms_shipped = 0
    AND i.type <> 0
    AND u.user_seeks IS NULL
   '
  INSERT INTO #TABLEINDEX
  EXEC (@SQL)
END
 

SELECT
    database_id AS DBID
,   dbName
,   SchemaName AS schName
,   tablename AS tabName
,   indexid AS idxID
,   IndexName AS idxName
,   type_desc AS type
,   IIF(is_primary_key = 1, 'PK', IIF(is_unique = 1, 'UK', '')) as KeyConst
,   KeyColumns AS KeyCols
,   IncludedColumns AS IncludeCols
,   user_seeks 
,   user_scans 
,   user_lookups
,   user_updates
FROM #TABLEINDEX
ORDER BY database_id, tablename, indexid
GO

 

주요 지표 내용 : 링크

user_seeks 사용자 쿼리별 검색(Seek) 수입니다.
user_scans 'seek' 조건자를 사용하지 않은 사용자 쿼리별 스캔 수입니다.
user_lookups 해당 인덱스를 통해 RID LookUP이 발생한 횟수입니다. 
user_updates 사용자 쿼리별 업데이트 수입니다. 영향을 받는 실제 행이 아닌 작업 수를 나타내는 삽입, 삭제 및 업데이트 포함됩니다. 예를 들어 한 문에서 1000개의 행을 삭제하는 경우 이 개수는 1씩 증가합니다.

user_seeks이 찍혀있다면, 해당 인덱스는 현재 사용 중이라는 의미입니다. 

이와 반대로 user_seeks 값이 0이거나 NULL이면 해당 인덱스는 사용되지 않고 있을 가능성이 높습니다.

 

 

6. 전체 인덱스 용량 조회 쿼리에서 만든 #temp2 테이블을 이용하면 아래 내용도 조회가 가능합니다.

SELECT A.database_id, A.dbname, A.SchemaName, A.tablename, A.indexid, A.indexname, A.type_desc, A.KeyColumns, 
     B.[Indexsize(KB)], user_seeks,user_scans,user_lookups,user_updates
    FROM #TABLEINDEX A
        INNER JOIN #temp2 B
        ON A.database_id = B.DBID
        AND A.dbname = B.DBName
        AND A.SchemaName = B.SchemaName
        AND A.tablename = B.TableName
GO