viernes, 26 de junio de 2015

Mantenimiento de índices- Identificar el porcentaje de fragmentación de los índices

Mantenimiento de índices – Identificar Fragmentación

Los índices como ya sabemos son de vital importancia para el motor de base de datos tanto para hacer consultas como procesos propios de manipulación de datos, igualmente la fragmentación de estos índices es una condición que se debe monitorear y solucionar como parte de la administración de las bases de datos.

La primera parte del mantenimiento debe ser monitorear la fragmentación de los índices para ellos es importante tener en cuenta los niveles de fragmentación que se pueden presentar:

A- Fragmentación Inocua: Se denomina a aquella fragmentación por la cual no deberíamos preocuparnos, estos casos son cuando el índice contiene menos de 1000 páginas o cuando la tabla es muy pequeña.

B- Fragmentación Baja: Corresponde a la fragmentación que no es superior al % 5 del índice. Para estos casos se recomienda no tomar acciones ya que puede ser mucho más el costo que el beneficio obtenido.

C- Fragmentación Media: es la fragmentación > % 5 y < = al %30. Se recomienda hacer un Reorganize de los índices.

D- Fragmentación Alta: es la fragmentación > al % 30. Se recomienda hacer un Rebuild de los índices.

Identificar el porcentaje de fragmentación de los índices

Para identificar la fragmentación de los índices el sistema ya cuenta con una vista prediseñada para esto sys.dm_db_index_physical_stats. Esta vista devuelve la información de tamaño y fragmentación de los datos y los índices de una tabla o vista especificada.

Para utilizar esta vista se deben proveer una serie de parámetros que corresponden a:

-          database_id: Identificador de la base de datos (NULL, 0 ó DEFAULT)

-          object_id: Es el identificador de objeto de la tabla o vista donde está activado el índice (NULL, 0 ó DEFAULT)

-          index_id: Es el identificador del índice. (NULL, 0, -1 ó DEFAULT)

-          partition_number: Es el número de partición en el objeto. (NULL, 0 ó DEFAULT)

-          Mode: Es el nombre del modo. (DEFAULT, NULL, LIMITED, SAMPLED o DETAILED)

Los valores entre paréntesis son los que se pueden usar por defecto

También utilizaremos sys.indexes que es una vista que contiene la información de los índices y las tablas o vistas que los contienen y la vista sys.objects que contiene la información de los objetos de la base de datos.

El script es el siguiente:

select CURRENT_TIMESTAMP as Fecha, DB_NAME(db_id()) as DatabaseName, @@servername Servidor, b.name as IndexName, obj.name as ObjectName,
a.avg_fragmentation_in_percent as '%Frag',  a.page_count as NumeroPaginas , a.fragment_count as PromPagFrag,a.index_type_desc as TipoIndice,
a.avg_fragment_size_in_pages, a.partition_number
FROM  sys.dm_db_index_physical_stats(db_id(), NULL, NULL, NULL, 'limited') as a
INNER JOIN sys.indexes as b
ON a.object_id = b.object_id AND a.index_id = b.index_id
INNER JOIN sys.objects as Obj
ON  a.object_id = Obj.object_id

El resultado de este script es algo así:


Como podemos ver el script nos dirá el porcentaje de fragmentación de cada índice de esta base, mostrándonos su nombre y en que tabla o vista esta creado, pero para poder capturar toda la información de todas las bases de datos de una instancia tendríamos que ejecutar el script en cada base de datos lo cual puede ser un poco tedioso por tal motivo sería mucho más sencillo implementar el procedimiento almacenado SP_MSFOREACHDB.

SP_MSFOREACHDB es un procedimiento almacenado de SQL Server que nos permite iterar con cada base de datos de una instancia para este caso en especial es muy oportuno, y para poder ver la información toda junta la introduciremos en una tabla temporal.

Primero crearemos la tabla temporal:

CREATE TABLE #INDEX(
       [fecha] [datetime] NULL,
       [dbname] [varchar](200) NULL,
       [SRV] [varchar](200) NULL,
       [INDICE] [varchar](500) NULL,
       [TABLA] [varchar](200) NULL,
       [FRAGM] [decimal](20, 5) NULL,
       [numeropaginas] [decimal](20, 5) NULL,
       [PROMPAGFRAG] [decimal](20, 4) NULL,
       [tipoindice] [varchar](200) NULL,
       [fragsizepages] [decimal](20, 5) NULL,
       [PARTS] [int] NULL
)
Los tipos de datos deben coincidir con los que arroja la consulta que planteamos antes
Posterior a crear la tabla temporal declararemos una variable que aloje el script que queremos ejecutar en todas las bases de datos
DECLARE @QUERY NVARCHAR(MAX)

Ahora le asignaremos a la variable el script que va a ejecutarse pero antes añadimos al script la sentencia ‘use ?’  teniendo en cuenta que ‘?’ funciona como un marcador para el nombre de la base de datos.

SET @QUERY = 'USE ? ;
select CURRENT_TIMESTAMP as hora, DB_NAME(db_id()) as DatabaseName, @@servername servidor, b.name as IndexName, obj.name as ObjectName,
a.avg_fragmentation_in_percent,  a.page_count, a.fragment_count,a.index_type_desc, a.avg_fragment_size_in_pages, a.partition_number
FROM  sys.dm_db_index_physical_stats(db_id(), NULL, NULL, NULL, "limited") AS a
INNER JOIN sys.indexes AS b
ON a.object_id = b.object_id AND a.index_id = b.index_id
INNER JOIN sys.objects as Obj
on  a.object_id = Obj.object_id'

Finalmente y una vez asignado el script utilizaremos el SP_MSFOREACHDB para que se ejecute en cada base de datos e inserte la información en la tabla temporal

INSERT INTO #INDEX
EXEC SP_MSFOREACHDB @QUERY

La ejecución nos dará como resultado algo así



Y si hacemos un select a la tabla temporal veremos



Para que sea más evidente haremos un select count distinct que nos permita ver cuantas bases de datos hay en la tabla



De esta manera y con este script podríamos hacer un monitoreo a la fragmentación de nuestros índices creando un Job que ejecute este script cada semana.


No hay comentarios:

Publicar un comentario