El desempeo de una aplicacin est directamente relacionado al buen o mal diseo de
los ndices de la base de datos.
Para demostrar esto tomemos como ejemplo la tabla de usuarios de un sistema cualquiera, la cual tiene la siguiente estructura:
Esta tabla no tiene ningn ndice creado, por lo cual SQL Server tratar la tabla como un HEAP. Un heap es una estructura de datos que almacena la posicin fsica en la que se almacen cada nueva fila dentro de las pginas asignadas a la tabla. Puesto que esta tabla no tiene ningn tipo de ndice, es bastante eficiente para agregar nuevas filas a la tabla pero muy ineficiente para encontrar una fila especfica, esto se debe a que es necesario leer toda la tabla para obtener el resultado deseado. Para ilustrar esto, realicemos el siguiente experimento: utilizando el procedimiento almacenado CrearUsuarios, crearemos 100.000 usuarios de los cuales el 10.000 no est activo y su fecha de creacin esta en los ltimos 600 das. exec CrearUsuarios 100000, 10000, 600 Ahora realicemos una consulta para validar el usuario al inicio de sesin del sistema. SELECT Passwor d, Act i vo FROM Usuar i o WHERE User name = ' Usuar i o123' La respuesta probablemente funcione bastante rpido, ya que al recin haber creado los datos, todas estas filas estn en memoria, pero veamos el plan de ejecucin de esta consulta presionando el botn de la barra de herramientas del SQL Server Management Studio y luego ejecutando la misma consulta.
Al pasar el mouse sobre el primer elemento de la izquierda se puede observar el costo de la consulta.
El costo estimado de la consulta es de 0.685675 y la forma de resolverlo fue un Table Scan, lo que implica leer toda la tabla. Para obtener ms informacin sobre el acceso a los datos requeridos para resolver la consulta ejecutamos la siguiente consulta: CHECKPOINT
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS SET STATISTICS IO ON
SELECT Password, Activo FROM Usuario WHERE Username ='Usuario123'
SET STATISTICS IO OFF Lo primero que pasa al ejecutar esta consulta es que SQL Server escribe todos los cambios pendientes al disco, la segunda instruccin elimina todos los datos que tiene en memoria. La combinacin de estas dos instrucciones obliga a SQL Server a leer todo desde disco nuevamente, es importante tener en cuenta que al ejecutar DBCC DropCleanBuffers se produce un fuerte impacto en el desempeo de todos los usuarios, por lo que no se debe utilizar en servidores de produccin. La tercera instruccin nos permite obtener informacin sobre las lecturas de datos requeridas para contestar una consulta. El resultado que se obtiene en el rea de mensajes es:
( 1 r ow( s) af f ect ed) Tabl e ' Usuar i o' . Scan count 1, l ogi cal r eads 774, physi cal r eads 3, r ead- ahead r eads 773, l ob l ogi cal r eads 0, l ob physi cal r eads 0, l ob r ead- ahead r eads 0. Esto nos dice que se accedi a la tabla Usuario, se hizo una lectura completa de la tabla 1 vez, lo que equivale a 774 pginas ledas, como cada pgina pesa 8Kb, 774 * 8kb es igual a 6 Mb, precisamente lo mismo que pesa la tabla completa. Las lecturas fsicas son las pginas que no se encontraban en memoria cuando SQL Server las necesitaba, en este caso fueron 3, algo extrao ya que vaciamos el cache antes de ejecutar la consulta por lo cual no debera existir ninguna pgina en memoria, pero SQL Server es ms inteligente y cada vez que tiene que leer pginas desde el disco, lo cual es una operacin muy costosa, lee tambin otras pginas cercanas que puedan ser tiles para responder la consulta que solicit la lectura. En este caso SQL Server fue capaz de Leer 773 de las 774 pginas utilizando este mecanismo de lecturas anticipadas (read-ahead). Las lob o Large Object Block, son operaciones relacionadas a datos que no se guardan junto con la fila como los campos de tipo TEXT, IMAGE y los tipos de datos que sobrepasen los 8000 bytes de la pgina. En este caso no hay operaciones de este tipo. Para mejorar el desempeo de las consultas se utilizan ndices, los ms utilizados son los Clustered y Non-Clustered. Existen otros 3 tipos de ndices que se utilizan para mejorar los tiempos de acceso a datos XML, a bsquedas de texto y de datos espaciales. A continuacin se muestra la disponibilidad segn la versin de SQL Server: Clustered Index Non- Clustered Index Full Text Index Xml Index Spatial Index SQL Server 2000 si si - - - SQL Server 2005 si si (1) si si - SQL Server 2008 si si (1 y 2) si si si (1) Permite definir Columnas Incluidas (2) Permite definir ndices Filtrados Los Clustered Indexes son ndices que controlan el orden fsico de las filas en la tabla, por lo cual solo puede existir uno para cada tabla. Los Non-Clustered indexes son ndices que mantienen un sub conjunto de las columnas de la tabla en orden. Estos indices no modifican el orden de las filas de la tabla, en lugar de esto mantienen una lista ordenada de referencias a filas de la tabla original. Para ilustrar la diferencia entre estos 2 tipos de ndices podemos decir que las pginas blancas de la gua telefnica tienen un clustered index por Apellido(s) y Nombres, con lo cual puedo buscar de forma muy eficiente el nmero de telfono de una persona si conozco sus apellidos y su nombre, una vez que lo encuentro obtendr su nmero de telfono en forma inmediata pues el numero est al lado del nombre. En el caso de las pginas amarillas de la gua telefnica la forma de buscar es un poco distinta, en este caso busco por rubro. Primero busco en un ndice, el cual me indica en qu pgina se encuentra la lista de empresas que satisfacen la condicin que busco. Esto mismo es lo que pasa cuando utilizo un ndice Non-Clustered index una vez que encuentro lo que quiero en el ndice debo ir a leer la fila especfica para obtener el resto de los datos. Veamos qu pasa cuando agregamos un ndice a la columna Username de la tabla usuario creada anteriormente.
El ndice ser Non-Clustered y nico puesto que no podemos tener ms de un usuario con el mismo nombre. Al volver a ejecutar la misma consulta, obtenemos lo siguiente:
El plan de ejecucin es un poco ms complicado pero esta consulta es 100 veces menos costosa que la anterior. Ahora la consulta utiliza el ndice para encontrar el RID, que es el identificador de la fila, con este RID hace una bsqueda en la tabla de tipo heap (RID Lookup). Esta reduccin de costo se explica por la cantidad de accesos a datos que esta consulta requiere, puesto que estos bajan desde 774 a solo 4 pginas lo que equivale 32 kb de datos contra 6192 kb, que eran necesarios antes de la creacin del ndice.
( 1 r ow( s) af f ect ed) Tabl e ' Usuar i o' . Scan count 0, l ogi cal r eads 4, physi cal r eads 1, r ead- ahead r eads 0, l ob l ogi cal r eads 0, l ob physi cal r eads 0, l ob r ead- ahead r eads 0. Veamos otra alternativa, si utilizamos un clustered index en lugar de un non-cluster index no ser necesario el lookup, por lo que el acceso ser ms eficiente.
Ahora el plan de ejecucin se ve as: El acceso se ve as: Tabl e ' Usuar i o' . Scan count 0, l ogi cal r eads 3, physi cal r eads 2, r ead- ahead r eads 0, l ob l ogi cal r eads 0, l ob physi cal r eads 0, l ob r ead- ahead r eads 0. Como se puede ver el costo de esta consulta ahora es la mitad que con el ndice non- clustered y en lugar de 4 lecturas lgicas tenemos slo 2. Es importante destacar que siempre recomendable utilizar tablas que utilicen un Cluster index en lugar de tablas que solo utilicen ndices non-cluster. El clustered index controla el orden fsico de las filas en la tabla, a diferencia de los ndices Non-Clustered que funcionan como una lista ordenada de identificadores de fila. La siguiente ilustracin muestra la estructura que tiene un clustered index.
Todas las tablas que tienen un clustered index tienen un nodo raz y muchos nodos en los niveles intermedios, estos a su vez pueden apuntar a nodos hojas o a otros nodos intermedios. Esta estructura forma un rbol (B-Tree) que permite encontrar cualquier fila en forma eficiente. La bsqueda parte desde el nodo raz, este nodo tiene una lista de llaves, se comparan estas llaves para encontrar el nodo de nivel intermedio que contenga un rango de llaves que cubra la llave que se est buscando. Luego se repite el proceso en los nodos intermedios hasta que se encuentre la pgina de datos que contenga la fila especfica. Para ilustrar este proceso, realicemos una bsqueda sobre un clustered index de la llave 123. Contenido del Nodo Raz (ID 0) Llave ID Nodo Intermedio 1 1 100 2 160 3 300 4 500 5 1000 6 Contenido del Nodo Intermedio (ID 2) Llave ID Nodo Intermedio 100 10 110 11 120 12 130 13 140 14 150 15 Contenido del Nodo Hoja (ID 12) Llave Columna 1 Columna 2 Columna N 120 AX06 10000 XXX 121 AX02 10004 XXX 122 AX07 10000 XXX 123 AX04 20000 XXX 124 AX08 9000 XXX 125 AX01 1 XXX Para obtener el resultado se lee el nodo raz, se busca el nodo intermedio que contiene a la llave 123, en este caso el nodo 2 contiene filas con llave entre 100 y 160. Luego se repite el proceso, el nodo 12 contiene filas con llaves entre 120 y 130. El nodo 12 es un pgina de datos por lo cual no necesitamos seguir buscando. Cada vez que se agrega una fila a la tabla, SQL Server debe insertar la nueva fila en la posicin correcta dentro del ndice, esto puede ser una operacin simple y eficiente si es que la pgina es la ltima del ndice o si la pgina tiene espacio disponible. Si la pgina no tiene espacio es necesario dividir la pgina en 2, algo conocido como Page Split, que deja 2 pginas con un 50% de utilizacin. Cada vez que se elimina una fila de la tabla, SQL Server eliminar la fila de la pgina, sin modificar ninguna otra pgina, lo cual limita el impacto de la operacin a 1 sola pgina pero causa que se desperdicie mayor porcentaje de las pginas, proceso conocido como fragmentacin. Algo aun ms costoso ocurre cuando se actualiza el valor de la llave del ndice clustered, en este caso SQL Server debe copiar la fila desde la pagina original, aplicar los cambios indicados en el update, insertar la fila en la nueva pgina y finalmente eliminar la fila desde la pgina original. anteriormente vimos como los clustered index controlan el orden fsico de las filas en la tabla. Ahora veremos cmo operan los ndices Non-Clustered. La siguiente ilustracin muestra la estructura que tiene un non-clustered index.
Al igual que en el caso de los clustered index, los non-clustered index tienen un nodo raz y muchos nodos en los niveles intermedios, estos a su vez pueden apuntar a nodos hojas o a otros nodos intermedios. La diferencia se presenta en los nodos hoja, estos tienen almacenados solo el Id del registro y no todo el registro, por lo que se hacer necesario hacer una bsqueda sobre el ndice cluster o sobre el heap para obtener el resto de las columnas de la fila. La bsqueda parte desde el nodo raz, este nodo tiene una lista de llaves, se comparan estas llaves para encontrar el nodo de nivel intermedio que contenga un rango de llaves que cubra la llave que se est buscando. Luego se repite el proceso en los nodos intermedios hasta que se encuentre la llave que identifica a la fila correspondiente a la llave del ndice. Para ilustrar este proceso, realicemos una bsqueda de la llave AX04 sobre un non- clustered index.
Paso 1.- Contenido del Nodo Raz (ID 0) Llave ndice ID Nodo Intermedio AA01 1 AG01 2 BA01 3 DB02 4 RF04 5 KJ 01 6 Paso 2.- Contenido del Nodo Intermedio (ID 2) Llave ndice ID Nodo Intermedio AG01 10 AJ 10 11 AP20 12 AZ30 13 BA20 14 BH50 15 Paso 3.- Contenido del Nodo Hoja (ID 12) Llave ndice Llave AP20 101 AQ10 50 AR12 160 AX04 123 AX24 145 AY25 12 Paso 4.- Hemos encontrado la fila (123), ahora debemos realizar una bsqueda utilizando ndice cluster, conocido como bookmark lookup en SQL Server 2000/2005 y como key lookup en SQL Server 2008. Llave ID Nodo Intermedio 1 1 100 2 160 3 300 4 500 5 1000 6 Paso 5.- Contenido del Nodo Intermedio (ID 2) Llave ID Nodo Intermedio 100 10 110 11 120 12 130 13 140 14 150 15 Paso 6.- Contenido del Nodo Hoja (ID 12) Llave Columna 1 Columna 2 Columna N 120 AX06 10000 XXX 121 AX02 10004 XXX 122 AX07 10000 XXX 123 AX04 20000 XXX 124 AX08 9000 XXX 125 AX01 1 XXX En SQL Server la Llave ndice puede ser una o varias columnas, siempre y cuando el largo combinado de estas columnas no supere los 900 bytes. Columnas Incluidas SQL Server 2005 agreg una nueva funcionalidad a los ndices non-clustered llamada Columnas Incluidas estas columnas no son parte de la llave ndice, por lo que no se mantienen ordenadas dentro del ndice y como consecuencia de esto solo es necesario almacenar su valor en el nodo hoja del ndice. En el ejemplo anterior, el paso 3 quedara as al agregar la Columna 2 como una columna incluida del ndice:
Llave ndice Llave Columna Incluida (Columna 2) AP20 101 12000 AQ10 50 12332 AR12 160 12344 AX04 123 20000 124 145 12233 125 12 12221 Las columnas incluidas tienen varias ventajas: Al utilizar columnas incluidas en el ndice es posible superar la limitacin de los 900 bytes para la llave del ndice, manteniendo un ndice eficiente. Las modificaciones al valor de una columna incluida es ms eficiente que la modificacin de una columna que es parte de la llave del ndice pues estas no requieren ser mantenidas en orden. Es posible crear Covering Indexes, que son ndices que incluyen todas las columnas requeridas para contestar una consulta especfica, que no requieren la bsqueda sobre el clustered index. Este tipo de ndice es igual o ms eficiente que u ndice cluster. ndices Filtrados SQL Server 2008 agreg otra mejora a los ndices non-clustered llamada ndices Filtrados. Estos ndices mantienen ordenados un sub conjunto de las filas de la tabla. El uso ms comn de este tipo de ndices se da cuando queremos crear un ndice sobre una columna que permite valores nulos. Al crear un ndice normal podemos desperdiciar una gran parte del espacio de ndice ordenando filas que tienen un valor nulo. A continuacin se describen algunos ejemplos de casos de uso para estos ndices filtrados: La columna fecha de trmino de contrato de la tabla empleado contiene una gran cantidad de filas con el valor nulo. Los estados intermedios son buenos candidatos para ser ndices filtrados. Es posible crear un ndice que solo contenga las rdenes de compra recibidas y en proceso, pero no las despachadas.