Indices SQL 2005

6
Valor añadido Danysoft | www.danysoft.com 1 Análisis de índices SQL Server 2005 Datos Subtítulo: Estadísticas de uso de índice Dirigido a: Administradores de Bases de Datos Área: Bases de Datos Autor: Pablo F. Dueñas Campo Resumen SQL Server 2005 nos ofrece unas consultas dinámicas nuevas que nos dan mucha información sobre los índices de una base de datos. Dichas consultas son aprovechadas por los informes que nos ofrece usando la herramienta Reporting Services. Al usar directamente las consultas, no vamos a obtener unos informes tan bonitos, pero sí tendremos mayor control sobre los datos que se muestran, aparte de poder obtener algún que otro extra. Introducción En el presente artículo vamos a analizar a fondo la nueva consulta dinámica que nos ofrece los datos de uso de los índices. Mediante los números que nos dé, podremos evaluar si tenemos demasiados índices, si algunos no se están usando y por lo tanto se pueden borrar o si es más costoso mantenerlos que el beneficio que dan. Al final se presenta la consulta que vamos a ir desglosando y que nos ofrece todos los datos sobre el uso de los índices. Índices El primer reto es conseguir todos los índices de la base de datos a la que estamos conectados. Para ello usaremos la vista de sistema sys.indexes. Si la consultamos directamente veremos que nos va a dar más índices de los que queremos examinar por dos razones: Da los índices incluso de las tablas del sistema. Para filtrarlas vamos a usar una propiedad extendida de los objetos que indica si es una tabla de usuario o de sistema. Como los índices se relacionan con la tabla a que pertenecen directamente mediante el campo object_id, no hace falta nada más para aplicar el filtro: ObjectProperty(I.[object_id], 'IsUserTable') = 1. También da los índices de tipo montón, que en realidad no son tales, sino la localización de las páginas de cada tabla. Por lo tanto, los filtramos también: I.[type] <> 0. De esta vista vamos a necesitar algunos campos que son importantes para el análisis posterior de los datos. El más obvio es el nombre del índice. Luego el que quizás sea el

description

Indices SQL 2005

Transcript of Indices SQL 2005

Page 1: Indices SQL 2005

Valo

r añ

adid

o D

anys

oft

| w

ww

.dan

ysof

t.co

m

1

Análisis de índices SQL Server 2005

Datos

Subtítulo: Estadísticas de uso de índice

Dirigido a: Administradores de Bases de Datos

Área: Bases de Datos

Autor: Pablo F. Dueñas Campo

Resumen

SQL Server 2005 nos ofrece unas consultas dinámicas nuevas que nos dan mucha información sobre los índices de una base de datos. Dichas consultas son aprovechadas por los informes que nos ofrece usando la herramienta Reporting Services. Al usar directamente las consultas, no vamos a obtener unos informes tan bonitos, pero sí tendremos mayor control sobre los datos que se muestran, aparte de poder obtener algún que otro extra.

Introducción

En el presente artículo vamos a analizar a fondo la nueva consulta dinámica que nos ofrece los datos de uso de los índices. Mediante los números que nos dé, podremos evaluar si tenemos demasiados índices, si algunos no se están usando y por lo tanto se pueden borrar o si es más costoso mantenerlos que el beneficio que dan. Al final se presenta la consulta que vamos a ir desglosando y que nos ofrece todos los datos sobre el uso de los índices.

Índices

El primer reto es conseguir todos los índices de la base de datos a la que estamos conectados. Para ello usaremos la vista de sistema sys.indexes. Si la consultamos directamente veremos que nos va a dar más índices de los que queremos examinar por dos razones:

• Da los índices incluso de las tablas del sistema. Para filtrarlas vamos a usar una propiedad extendida de los objetos que indica si es una tabla de usuario o de sistema. Como los índices se relacionan con la tabla a que pertenecen directamente mediante el campo object_id, no hace falta nada más para aplicar el filtro: ObjectProperty(I.[object_id], 'IsUserTable') = 1.

• También da los índices de tipo montón, que en realidad no son tales, sino la localización de las páginas de cada tabla. Por lo tanto, los filtramos también: I.[type] <> 0.

De esta vista vamos a necesitar algunos campos que son importantes para el análisis posterior de los datos. El más obvio es el nombre del índice. Luego el que quizás sea el

Page 2: Indices SQL 2005

Valo

r añ

adid

o D

anys

oft

| w

ww

.dan

ysof

t.co

m

2

más importante, pues nos dice el tipo de índice que estamos viendo y, sobre todo, si es agrupado. Esto va a incidir en su uso, dado que un índice agrupado no es otra cosa que la tabla ordenada, amén del índice, por lo que habrá un dato adicional como son los «lookups» que se realizan en ellos. También es importante saber si el índice es único, pues aunque no se use y cueste su mantenimiento, son imprescindibles para obligar a que se cumpla dicha restricción. Por último, he añadido si el índice es hipotético o no, aunque no le daremos uso por ahora.

Esquema y Tablas

A continuación es importante obtener el esquema y la tabla a la que pertenece el índice. Como hemos manifestado, el campo Object_Id nos dice el identificador de la tabla a la que pertenece. Una vez que tenemos la tabla, mediante el identificador del esquema, podemos obtenerlo. Así incluimos los tres campos que identifican al índice dentro de la base de datos que estamos analizando.

De estos tres datos vamos a obtener otros dos adicionales. El primero es que vamos a numerar las tablas para una distinción adicional de cada una de ellas y para que así nos sea más fácil encontrarlas. Para ello usamos la función Dense_Rank que nos devuelve la posición de cada fila dentro de la ordenación que le pasamos. No usamos la función Rank, como en el dato siguiente, porque queremos que la numeración no tenga saltos. La siguiente columna nos numera los índices dentro de cada tabla. Para ello usamos la posición de cada fila dentro del esquema, tabla e índice, que nos va a dar una numeración seguida (sin saltos) porque estos tres campos identifican unívocamente cada fila. Se hace una partición en cada esquema y tabla para lograr nuestro objetivo.

Estadísticas de uso

La vista dinámica muestra la información que se lleva sobre el uso de índices. Devuelve los datos de una caché dentro de SQL Server que no se guarda en disco. Es decir, está vacía cada ver que una instancia de servidor comienza y se pierden cada vez que es cerrada. Por lo tanto, se guarda información sobre el uso de índices sólo desde que se abrió la base de datos a la que pertenecen. Además, sólo se guarda información de aquellos índices que se usan, por lo que puede haber índices en sys.indexes que no aparezcan en sys.dm_db_index_usage_stats.

De lo anteriormente dicho, deducimos que la unión con las tablas ya relacionadas debe ser un LEFT JOIN. Además, esta vista nos ofrece datos de todos los índices de la instancia, por lo que debemos restringirla a la base de datos a la que estamos conectados para que no nos traiga algún dato que pueda tener de otra: U.[database_id] = (SELECT db_id()).

Como se puede apreciar en la consulta, aquellos índices que no aparezcan con datos, son los que no se han usado desde que se inició la instancia de SQL Server. Por lo tanto, ¿se pueden eliminar? Depende. Los índices no agrupados que sobran, restan velocidad de varias maneras:

• Cada vez que un récord es insertado en su tabla, hay que insertar otro también en el índice. Esto es actividad de E/S adicional, sobre todo si se divide una página porque ya no caben más entradas. Además, hay que mantener el árbol balanceado.

Page 3: Indices SQL 2005

Valo

r añ

adid

o D

anys

oft

| w

ww

.dan

ysof

t.co

m

3

• Cada vez que se borra un récord de su tabla, hay que borrar otro del índice. Aunque esto no es tan costoso, es actividad del procesador y de E/S adicional.

• Cada vez que se actualiza una fila, la tabla tiene un índice agrupado y alguno de los valores de la clave cambia (cosa que no debería hacerse), hay que actualizar la entrada correspondiente en el índice.

• Si la tabla no tiene un índice agrupado o no cambia la clave, y sí cambia alguno de los campos del índice, hay que actualizarlo y recolocar dicha entrada. Si lo que cambia es una de las columnas incluidas, hay que actualizar su valor.

• Si se crea un índice agrupado, entonces todos los índices no agrupados deben cambiar los identificadores de fila físicos por los lógicos de dicho índice nuevo.

Como vemos, bastante actividad adicional para mantener los índices. Sin embargo, afirmamos que depende, que no siempre hay que eliminarlos. Por eso incluimos las columnas adicionales de sys.indexes, porque un índice agrupado, una clave primaria, un índice único o una clave externa cumplen misiones que justifican su existencia más allá de que cómo se usen en las consultas.

Funcionamiento de la caché

Cada búsqueda, recorrido, vistazo o actualización en cualquiera de los índices, bien sea originado por una consulta de usuario o por el sistema (por ejemplo, para recabar estadísticas), es contado como un uso de dicho índice e incrementa su contador correspondiente. En el caso de que aún no haya una entrada, se crea con todos los datos a cero, y se procede al incremento mencionado.

El contador de actualizaciones indica el mantenimiento del índice causado por las operaciones de inserción, actualización o borrado en la tabla o vista a la que pertenece, y que afecten a dicho índice.

Ya hemos dicho que los contadores se vacían cada vez que se inicia el servicio de SQL Server. Además, cada vez que una base de datos es separada o puesta fuera de conexión, también se vacían todos los contadores asociados con dicha base de datos.

Datos de la consulta

Si vemos las columnas que nos devuelve la consulta, aparecen dos grandes divisiones entre la parte originada por consultas de usuario y la originada por consultas del sistema. Ambas partes muestran la siguiente información por cada índice:

• El número de veces que un índice ha sido usado para una operación de búsqueda («seek»), tanto si es para una sola fila o si es para un recorrido ordenado. Es decir, cuántas veces se ha usado el índice como tal, llegando a las hojas a través de las ramas. También da el día y hora de esta última operación.

• El número de veces que se ha usado para un recorrido desordenado («scan»). Es decir, el número de veces que se ha recorrido este índice en vez de la tabla, al tener menos campos y, por lo tanto, deberse visitar menos páginas. Igualmente muestra el día y hora de la última operación.

• El número de veces en que se usó un índice agrupado para una operación «lookup». Esto es, cuando un índice no agrupado se usa para la búsqueda y el

Page 4: Indices SQL 2005

Valo

r añ

adid

o D

anys

oft

| w

ww

.dan

ysof

t.co

m

4

índice agrupado que nos ocupa suple el resto de los campos porque el primero no cubría la consulta. Como los anteriores, lo complementa con el día y la hora de la última operación.

• El número de veces que se tuvo que actualizar («update»), sea por una inserción, actualización o borrado. También con el día y hora de la última operación.

Beneficios

Al analizar la consulta que presentamos podemos ir deduciendo lo siguiente:

• Lo primero es, como ya se ha comentado, si una tabla tiene exceso de índices. La segunda columna los cuenta y cuando son demasiados, debemos empezar a preguntarnos cuántos sobran o si hay algo mal diseñado en la tabla o en la base de datos. ¿Cuántos son excesivos? Esto origina muchas discusiones. En principio, las tablas deberían usarse mediante su clave primaria, más allá de esto, depende mucho del programa que llame a la base de datos.

• Lo más aparente son los índices con columnas en blanco. Como hemos comentado, indican los que no se han usado desde que se inició la base de datos. Son los primeros candidatos a ser eliminados. Claro, como ya hemos dicho, siempre que no sirvan para forzar la unicidad, sean clave primaria, clave externa, etc.

• Seguidamente son los índices que se usan muy poco. Hay que examinarlos para ver si merece la pena mantenerlos. Por ejemplo, sirve para un informe que se hace una vez al día, pero que se necesita que sea rápido. Se usa poco, pero es necesario. En caso contrario, mejor eliminarlos.

• El contador de actualizaciones indica el mantenimiento del índice. Se puede usar para ver si el índice está causando más problemas que beneficios. Aunque esta columna hay que verla con cuidado: cada vez que hay una actualización que lo afecta o un borrado, se va a hacer una entrada en la columna de búsquedas para ir la hoja que hay que actualizar o eliminar y otra en la de actualizaciones. Por lo tanto, una tabla donde haya muchas actualizaciones o borrados va a dar datos parejos en ambas columnas, cuando sólo está dando problemas. De nuevo, hay que ver su uso.

Además de que la consulta presenta más datos que los informes de SQL Server, también permite filtrados y ordenaciones. Por ejemplo, podemos querer ver sólo los índices que no se han usado nunca, los que no se han usado desde determinada fecha, los que se han usado poco, los pertenecientes a una tabla, incluso usar una fórmula para evaluarlos teniendo en cuenta la mayor utilidad de una búsqueda que la de un recorrido (como se muestra comentado en la consulta) o correlacionando los usos del índice son su mantenimiento. Son formas más rápidas de analizar los índices que un informe completo.

Conclusión

Las nuevas consultas dinámicas nos ofrecen una muy valiosa información sobre el trabajo que hace SQL Server internamente. En concreto la sys.dm_db_index_usage_stats nos es muy útil para evaluar la conveniencia de mantener, estudiar o eliminar un índice. Hay que recordar que las optimaciones que

Page 5: Indices SQL 2005

Valo

r añ

adid

o D

anys

oft

| w

ww

.dan

ysof

t.co

m

5

hace la base de datos son dinámicas y cambian en el tiempo. Usando esta consulta de vez en cuando, podemos ver qué índices han dejado de ser útiles o cuáles están dando más trabajo mantenerlos que lo que ayudan en las consultas. Son, por tanto, imprescindibles para el administrador de bases de datos y bienvenidas en SQL Server 2005.

Apéndice: Consulta SELECT Dense_Rank() OVER (ORDER BY S.Name, T.Name) AS L1, Rank() OVER (PARTITION BY S.Name, T.Name ORDER BY I.Name) AS L2, S.Name AS [Esquema], T.Name AS [Tabla], I.Name AS [Índice], CASE I.Type WHEN 1 THEN 'Agrupado' WHEN 2 THEN 'No agrupado' WHEN 3 THEN 'XML' END AS [Tipo de Índice], CASE I.Is_Unique WHEN 1 THEN 'Sí' ELSE 'No' END AS [Único], CASE I.Is_Hypothetical WHEN 1 THEN 'Sí' ELSE 'No' END AS [Hipotético], CASE WHEN U.User_Seeks IS NULL THEN '' ELSE Convert(VARCHAR, U.User_Seeks) END AS [Búsquedas de Usuario], CASE WHEN U.User_Scans IS NULL THEN '' ELSE Convert(VARCHAR, U.User_Scans) END AS [Recorridos U], CASE WHEN U.User_Lookups IS NULL THEN '' ELSE Convert(VARCHAR, U.User_Lookups) END AS [Lookups U], CASE WHEN U.User_Updates IS NULL THEN '' ELSE Convert(VARCHAR, U.User_Updates) END AS [ActualizacionesU], CASE WHEN U.Last_User_Seek IS NULL THEN '' ELSE Convert(VARCHAR, U.Last_User_Seek, 113) END AS [Última Búsqueda de Usuario], CASE WHEN U.Last_User_Scan IS NULL THEN '' ELSE Convert(VARCHAR, U.Last_User_Scan, 113) END AS [Último Recorrido U], CASE WHEN U.Last_User_Lookup IS NULL THEN '' ELSE Convert(VARCHAR, U.Last_User_Lookup, 113) END AS [Último Lookup U], CASE WHEN U.Last_User_Update IS NULL THEN '' ELSE Convert(VARCHAR, U.Last_User_Update, 113) END AS [Última Actualización U], CASE WHEN U.System_Seeks IS NULL THEN '' ELSE Convert(VARCHAR, U.System_Seeks) END AS [Búsquedas del Sistema], CASE WHEN U.System_Scans IS NULL THEN '' ELSE Convert(VARCHAR, U.System_Scans) END AS [Recorridos S], CASE WHEN U.system_Lookups IS NULL THEN '' ELSE Convert(VARCHAR, U.System_Lookups) END AS [Lookups S], CASE WHEN U.System_Updates IS NULL THEN '' ELSE Convert(VARCHAR, U.System_Updates) END AS [Actualizaciones S], CASE WHEN U.Last_System_Seek IS NULL THEN '' ELSE Convert(VARCHAR, U.Last_System_Seek, 113) END AS [Última Búsqueda del Sistema S],

Page 6: Indices SQL 2005

Valo

r añ

adid

o D

anys

oft

| w

ww

.dan

ysof

t.co

m

6

CASE WHEN U.Last_System_Scan IS NULL THEN '' ELSE Convert(VARCHAR, U.Last_System_Scan, 113) END AS [Último Recorrido S], CASE WHEN U.Last_System_Lookup IS NULL THEN '' ELSE Convert(VARCHAR, U.Last_System_Lookup, 113) END AS [Último Lookup S], CASE WHEN U.Last_System_Update IS NULL THEN '' ELSE Convert(VARCHAR, U.Last_System_Update, 113) END AS [Última Actualización S] FROM sys.indexes AS I -- Indexes INNER JOIN sys.tables T -- Tables ON (T.[Object_Id] = I.[Object_Id]) INNER JOIN sys.schemas S -- Schemas ON (S.[Schema_Id] = T.[Schema_Id]) LEFT JOIN sys.dm_db_index_usage_stats AS U -- Usage Statistics ON U.[database_id] = (SELECT db_id()) AND U.[Object_Id] = I.[Object_Id] AND U.[Index_Id] = I.[Index_Id] WHERE I.[type] <> 0 AND ObjectProperty(I.[object_id], 'IsUserTable') = 1 -- AND T.Name LIKE 'Department' ORDER BY S.Name, T.Name, I.Name; --ORDER BY U.User_Seeks * 10 + U.User_Scans