Anda di halaman 1dari 16

Consejos para mejorar el rendimiento

en el acceso con SQL Server


Por Antonio Quirós

Desde que hace unos pocos años Microsoft comenzó su paulatina conquista del mercado cor-
porativo con su producto abanderado Windows NT, era de suponer que si lograba una situa-
ción de preeminencia en el mismo ésta debía pasar por la labor de prestigiar su producto para
la gestión de bases de datos; estamos hablando de SQL Server.

La mayoría de las aplicaciones informáticas desarrolladas para el mercado corporativo son


aplicaciones de gestión que tienen como soporte fundamental un Sistema Gestor de Bases de
Datos. UNIX es, en ese segmento, el principal sistema operativo y, desde luego, Oracle la he-
rramienta de base de datos más usada. Sin embargo, a lo largo de estos años Windows NT ha
conseguido una importantísima cuota de mercado y, en paralelo a él, SQL Server se ha ido
convirtiendo en el motor de datos con mayor presencia en dicho entorno y, por tanto, en uno
de los de mayor presencia en el mercado.

En su origen el SQL Server de Microsoft parte del SQL Server de Sybase. Sin embargo, tras la
adquisición del mismo por parte de Microsoft ambos productos, aún conservando muchas co-
sas comunes, han ido separándose poco a poco. Acorde con esta situación, el primer SQL
Server de Microsoft –tal como el de Sybase- era multiplataforma (existían versiones para DOS,
Windows, Novell y OS/2), pero con el paso del tiempo el producto de Microsoft fue ciñéndose al
sistema operativo de su propietario, Windows NT abandonando las otras versiones, mientras
que el de Sybase ha continuado claramente dentro de la línea multiplataforma.

Esto ha marcado con claridad el perfil de SQL Server. Hoy es, quizá, el único producto de base
de datos basado en la tecnología multisubproceso (multithreading) de NT por contraposición a
la arquitectura multiproceso de la que participan la mayoría de los productos de su competen-
cia. ¿Qué virtud supone esto para SQL Server? Pues es bien sencillo, se trata de que, por la
razón mencionada, SQL Server es el producto que mejor se acomoda a la mecánica de trabajo
de Windows NT y, por tanto, con el que mejor resultado podemos obtener.

Por otro lado, SQL Server se ha hecho muy popular entre los desarrolladores de software. Su
buen entronque con los productos de desarrollo de Microsoft, Visual Basic y Visual C++, ha
hecho que existan muchas aplicaciones desarrolladas para este entorno a través de la tecnolo-
gía cliente/servidor.

Sin embargo, muchos de los desarrolladores que trabajan con el producto tienen un conoci-
miento muy superficial del mismo. SQL Server, como la mayoría de los productos de bases de
datos, trata de cumplir con los estándares que para SQL existen y, por tanto, sin tener un pro-
fundo conocimiento de su mecánica de almacenamiento de datos, nos podemos permitir tra-
bajar con él sin grandes riesgos. No obstante, conforme las aplicaciones van enfrentándose a
situaciones más complejas respecto a los datos, se nos hace necesario conocer con más pro-
fusión los caminos a seguir para optimizar recursos.

El objetivo que perseguimos en esta conferencia va en esa dirección. Trataremos en ella de


contar las técnicas, referidas sobre todo a la indexación de datos, que nos ayudarán a ganar en
rapidez a la hora de actualizar/acceder a la información contenida en bases de datos SQL Ser-
ver. Pero como para poder tratar sobre esta materia es necesario también conocer la estructura
interna de los datos almacenados, dedicaremos también una pequeña parte de esta charla a
describir la misma aunque no con la profusión que podemos encontrar en otra bibliografía más
centrada en este aspecto.

La arquitectura de almacenamiento de datos en SQL Server


Al principio de enfrentamos a SQL Server pensamos que las categorías lógicas con las que
superficialmente trabajamos son reales y que los datos realmente se almacenan en bases de

1
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
datos que contienen tablas, índices y otros objetos. Por último, las tablas se dividen en filas y
éstas en columnas. Esto no es realmente así, o no lo es del todo. Efectivamente, en SQL Ser-
ver la unidad básica de almacenamiento de datos es la base de datos. Sin embargo las bases
de datos no son dispositivos físicos sino lógicos, como tales se encuentran contenidas en lo
que denominamos Database Device; éstos sí son realmente dispositivos físicos. El lector pue-
de comprobar que cada Database Device definido para SQL Server se corresponde con un
archivo en disco que se ubica en el directorio \mssql\data de la instalación de SQL Server. Los
Database Device sirven para almacenar las distintas bases de datos y cada base de datos
contiene distintas tablas e índices. No obstante, insistimos, los conceptos de base de datos,
tabla e índice son lógicos y no físicos.

La noción de página
La unidad real de almacenamiento físico dentro de un Database Device es lo que se denomina
la página. Toda la información en SQL Server se almacena en páginas de 2 KB. Cada página
tiene 32 bytes de cabecera conteniendo:

• El número lógico que la identifica


• Referencias a las páginas anterior y posterior
• El object_id del objeto contenido en la página
• El tamaño mínimo de fila
• El próximo número de fila dentro de la página
• La localización del byte de comienzo del espacio libre en la página

Qué es una página


La página es la unidad mínima de almacenamiento de datos en SQL
Server. Cada página tiene un tamaño de 2 KB y puede almacenar filas
de una tabla o de un índice.

Las páginas se agrupan en grupos de 256 páginas contiguas en lo que se denomina una Allo-
cation Page. Cada una de estas unidades tiene una primera página que realiza las funciones
de índice del resto de las páginas contenidas en la misma. Dentro de estas unidades, las pági-
nas se colocan en bloques contiguos de 8 páginas. Esto es lo que se denomina extent. En una
unidad de asignación hay, por tanto, 32 extent. Al crear una tabla, ésta toma como tamaño
mínimo (aunque no tenga ninguna fila, un extent o lo que es lo mismo, 16 KB.

Qué es un extent
Un extent es un bloque de 8 páginas contiguas. Cada extent mide, por
tanto, 16 KB. Este es, asimismo, el tamaño mínimo que una tabla, aun-
que no tenga ninguna fila, puede tener.

Qué es una allocation page


La allocation page es un conjunto 256 páginas (o lo que es lo mismo 32
extent) de forma que la primera página hace de índice para poder locali-
zar las 255 restantes.

2
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Por último, hay que reseñar que cada página puede contener como máximo 256 filas de
datos.

Por tanto, la imagen que debemos hacernos de cómo se almacenan físicamente los datos
en SQL Server es la de una cadena de páginas contenedoras de filas, tal que en cada una
de ellas hay referencias a la anterior y las posterior de forma que la secuencia real de los
datos sea fácilmente perseguible. Para su mejor ubicación estas páginas se agrupan en
extents y éstos en allocation pages.

Cada vez que una consulta nuestra requiere leer una fila, el sistema, a través de las es-
tructuras de índices necesarias, localiza la página donde se halla esa fila la carga en me-
moria y nos devuelve los valores requeridos. Igualmente, cuando estamos actualizando
información en la base de datos, las páginas se actualizan de forma se van llenando de fi-
las y dividiendo en páginas nuevas según el sistema lo va requiriendo.

Obviamente, de esta manera de organizar las cosas sacaremos muchas lecciones para
optimizar el tratamiento de los datos en SQL Server. Pero demos tiempo al tiempo.

Los índices en SQL Server


1
Una de las reglas de Codd respecto a la estructura relacional de datos nos dice que la estruc-
tura lógica de los datos ha de ser independiente de la mecánica física con que dichos datos se
almacenen. Debido al cumplimiento de esta regla, todos los fabricantes de bases de datos re-
lacionales nos facilitan, a través de la sintaxis estándar de SQL, la creación de bases de datos
sin preocuparnos en exceso de qué índices hemos de usar para que el acceso a dichos datos
sea óptimo.

Normalmente, el diseñador de la base de datos se ocupa en primer término de definir las tablas
con sus correspondientes columnas y las claves primarias y ajenas para cada una de ellas. La
mayoría de los sistemas (y esto incluye a SQL Server) poseen una mecánica automática de
creación de índices en el mismo momento que una de estas claves ha sido definida.

Sin embargo, la mayoría de las veces, esta mecánica queda pobre. Si queremos establecer
mejores y más rápidos sistemas para acceder a los datos es bastante conveniente que conoz-
camos la mecánica de manejo de índices que cada sistema realiza y que hagamos manual-
mente cuantas aportaciones sean necesarias a esa creación automática aportada por el siste-
ma.

Sentado esto nos queda por determinar, pues, cómo son los índices en SQL Server, cómo
podemos trabajar con ellos y cuales son más adecuados para cada tipo de trabajo.

Los índices en SQL Server son índices B-Tree totalmente similares en su concepción y estruc-
tura a los de cualquier otro producto similar. Estos índices poseen una estructura de árbol de
modo que los datos en ellos se depositan en hojas accesibles a través de una organización de
ramas. Un árbol B-Tree normalmente posee una root page o página raíz donde se encuentra,
por así decirlo el índice principal del árbol. Cada línea de dicha página apunta a otra página
conteniendo otros índices secundarios (node pages) que sirven para acceder más rápidamente
a los datos. Desde estos índices secundarios podemos ir a otros índices secundarios o ya di-
rectamente a las hojas (leaf pages) del árbol donde se contiene la referencia a la información
que estamos buscando.

Veamos un hipotético sistema de ejemplo. Tenemos una tabla de clientes que contiene 100
filas con datos de 100 clientes distintos. Si no usamos ningún sistema de indexación, cuando
deseamos acceder, por ejemplo, al cliente 78, tenemos que procesar secuencialmente la tabla
y recorrerla fila a fila hasta llegar al cliente que necesitamos. Contando con que los datos estu-

1
Las reglas de Codd junto con un monumental conjunto de excelente material sobre teoría de
bases de datos puede verse en Concepción y Diseño de Bases de Datos, Adoración de Mi-
guel y Mario Piattini, editorial RA-MA, Madrid, 1992.

3
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
vieran ubicados en orden en la tabla necesitaríamos 78 accesos hasta que se cumpliera la
condición de que el valor que buscamos (78) se corresponde con el código de cliente examina-
do. Pero, pensemos ahora en una técnica que nos ayude a conseguir esto mismo, pero con
menos accesos. Supongamos que podemos montar un sistema de indexación B-Tree y que
nuestro sistema permite que cada hoja del árbol pueda contener hasta un máximo de 5 filas.
Podíamos montar la lógica de nuestros datos del siguiente modo: primero una página raíz
contenido un índice de donde se encuentran realmente los datos. Por ejemplo:

1 Node page 1
21 Node page 2
41 Node page 3
61 Node page 4
81 Node page 5
Root page

El lector verá que lo que hemos hecho ha sido partir la totalidad de las 100 filas en 5 porciones
(el espacio que hemos definido para nuestra página B-Tree teórica). La primera fila de esta
página nos servirá si deseamos localizar un cliente comprendido entre el 1 y el 20, la segunda
entre el 21 y el 41 y así sucesivamente.

Veamos ahora como serían cada una de esas node pages 1 a 5, por ejemplo la 1.

1 Leaf page 1.1


5 Leaf page 1.2
9 Leaf page 1.3
13 Leaf page 1.4
17 Leaf page 1.5
Node page 1

Como puede verse ahora hacemos lo mismo, es decir, las 20 filas contenidas en la primera
entrada de la root page las volvemos a dividir en bloques de 5 filas, de modo que ahora la pri-
mera entrada de la tabla nos servirá para localizar clientes cuyo código esté comprendido entre
el 1 y el 4, la segunda entre el 5 y el 8 y así sucesivamente.

Veamos cómo sería la página 4 (de las reseñadas en el raíz) ya que dicha página es la que
vamos a necesitar para localizar el cliente 73 del que antes hemos hablado.

61 Leaf page 4.1


65 Leaf page 4.2
69 Leaf page 4.3
73 Leaf page 4.4
77 Leaf page 4.5
Node page 4

Veamos, por último, como sería la Leaf page 4.4, ya que ésta es la que nos servirá para ubicar
al cliente que buscamos.

73 Data page xx
74 Data page xx
75 Data page xx
76 Data page xx
Leaf page 4.4

4
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
¿Cuántos accesos necesitamos ahora con este sistema para localizar al cliente 73? Pues bien,
primero cargaríamos la root page y necesitaríamos 5 accesos para saber que el dato está refe-
renciado en la node page 4. Cargamos dicha página y necesitamos 5 accesos más para saber
que el dato está referenciado en la leaf page 4.4. Cargamos dicha página y con 1 sólo acceso a
la misma ya sabemos que el cliente se encuentra en la Data Page xx (esta es ya una página de
datos de una tabla y no de un índice), así, pues, con un acceso más para llegar a esa página
habremos terminado nuestra búsqueda. En total hemos necesitado 12 accesos cuando antes
necesitamos 73. En un sistema pequeño como este (100 filas) la diferencia es notoria, pero una
tabla con centeneres de miles o millones de filas la diferencia es abismal.

Ni que decir tiene que aquí hemos pensado, para que sirva de ejemplo, un sistema absoluta-
mente sencillo que se caracteriza, además, por tener sólo uno nivel de node pages. No obs-
tante, cuanto más grande es un índice existen más páginas de nodo que hacen referencia a
otras páginas de nodo, y así sucesivamente, de forma que el tratamiento es más lento al nece-
sitarse más accesos.

De todo este trabajo tenemos que sacar algunas consecuencias importantes.

1. Los índices nos ayudan a localizar los datos con mucha rapidez, por tanto cuando necesi-
tamos que nuestras aplicaciones hagan selecciones de forma rápida lo mejor es que ten-
gamos una buena estructura de índices diseñada.
2. La labor de mantenimiento de un índice es compleja. El gestor de bases de datos ha de
mantener no sólo la información contenida en las tablas sino también la contenida en los
índices, por tanto cada actualización a la base de datos se hace más lenta. Fíjese el lector
que la forma más rápida de insertar datos en una tabla es cuando ésta no posee ningún ín-
dice. Los datos se van añadiendo en la forma de páginas antes reseñada, por el final de las
que ya existan asociadas a la tabla. Si hay hueco en las páginas existentes, las filas se
añaden ahí y si no lo hay se crean nuevas páginas. Cuando existe un índice, además de
hacer lo anterior hay que proceder a actualizar la estructura de árbol del índice, Si la fila in-
sertada cabe en la leaf page donde le corresponda estar, el tema es más o menos rápido,
pero si esto no es así, el sistema ha de andar partiendo páginas existentes en dos de forma
que el dato pueda ser incorporado en los espacios libres que queden en las dos nuevas
páginas productos de la partición de una llena. Asimismo, han de actualizarse las referen-
cias en las node pages y root page si procede.
3. En cualquier caso, hay que tratar que los niveles de profundidad de las node pages sean
los menores posibles de forma que los accesos sean más rápidos. Esto podemos conse-
guirlo tanto a nivel lógico, definiendo claves lo más cortas posibles, como a través de las
herramientas informativas que SQL Server nos proporciona y que luego veremos.

5
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Consejos y trucos
Si nuestra aplicación es de tipo transaccional (es decir que para ella son
críticas las actualizaciones) lo mejor es que reduzcamos los índices al
menor número posible, ya que de este modo las actualizaciones irán
mucho más rápidas. Sin embargo, si lo más crítico para nuestra aplica-
ción son las consultas, lo mejor es que nuestra estructura de índices esté
altamente estudiada y sea altamente compleja de forma que cubra todos
los casos de selección posibles.
Pero ¿cómo aunar ambos criterios en aplicaciones híbridas? Bueno,
pues, siempre hay un justo término medio que cada diseñador de BD ha
de saber evaluar. Si esto no es posible quizá una buena táctica sea dis-
poner de dos bases de datos, una para las transacciones y otra para las
consultas. La segunda se actualiza, por ejemplo, cada noche a través de
un proceso automático y, se diferencia de la primera, en que contiene
una estructura de índices mucho más compleja.

Indices CLUSTERED versus índices NON-CLUSTERED


SQL Server nos proporciona dos tipos de índices. Los CLUSTERED y los NON-CLUSTERED.
¿Qué diferencia existe entre ambos? Bien, trataremos de explicarlo. En un índice clustered no
existen las leaf pages de la estructura B-Tree antes mencionada. A cambio estás páginas se
sustituyen directamente por data pages, es decir, por páginas reales de la tabla. De esta forma
las node pages hacen referencia directamente a data pages, por lo que nos ahorramos el nivel
más pesado (el que más información contiene) de la estructura B-Tree. Ahora bien, para que
esto funcione, los datos de la tabla para la que aportamos un índice clustered han de estar
ordenados en función de la clave del índice clustered. De esta forma lo que logramos es que
las data pages funcionen como si fueran las leaf pages de la estructura B-Tree.

La diferencia entre ambos tipos de índice se muestra de forma comparada en las figuras 1 y 2.

6
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Figura 1

Figura 2

7
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Para mantener la estructura de un índice clustered SQL Server necesita mantener siempre
ordenados en función de la clave de dicho índice, los datos presentes en la tabla. En tanto que
una tabla sólo puede contener los datos de una única forma, sólo es posible mantener un único
índice clustered por tabla, mientras que índices non-clustered pueden mantenerse tantos como
se deseen. Debido a todo esto los índices clustered nos permiten siempre más rapidez en el
acceso a los datos, pero también son más costosos de mantener en tanto que necesitan
mantener siempre una versión ordenada de los datos presentes en la tabla.

De todo lo visto se deduce lo importante que es el proceso de seleccionar qué tipo de índice
deseamos para las distintas columnas de nuestra base de datos. No es menos importante, el
seguimiento posterior del rendimiento del índice. SQL Server nos da alguna información im-
portante a este respecto. Por ejemplo, veamos en la figura 3 algunos datos de un índice non-
clustered. A esta información accedemos pulsando el botón derecho del ratón sobre la tabla
correspondiente y seleccionando Indexes.

Como puede verse en la información presente en la ventana se nos informa que el índice ocu-
pa 1032 KB. Igualmente se nos indica que el índice se creó con un factor de relleno de 0 (más
tarde explicaremos este concepto). Asimismo se nos dan unos valores mínimos, medios y má-
ximos muy importantes, se trata de:

• El tamaño del índice


• Las filas por cada leaf page y por cada node Page del mismo
• Los niveles que ha alcanzado el árbol B-Tree.

Si reconstruimos el índice para convertirlo en clustered el lector notará como cambian los valo-
res que ahora pueden verse en la figura 4.

Figura 3: Datos de un índice non-clustered

8
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Figura 4: Datos de un índice clustered

El lector notará la diferencia inmediata en las cifras. Ahora sólo tenemos dos niveles (lógica-
mente ahora no existen leaf pages). Como consecuencia de esto el tamaño del índice ha baja-
do considerablemente (de 1032 KB a 16 KB).

Indices clustered y recuperación de espacio


Un aspecto muy importante en los índices clustered es que al tener que mantenerse siempre la
tabla asociada de modo ordenado, cada fila que se inserta recupera el espacio dejado por otras
filas borradas, ya que todo esto se recicla cada vez que se produce una reordenación de las D
data pages. Esto no sucede en las tablas con índices non-clustered donde la recuperación de
espacio se produce sólo cuando explícitamente la tabla es reorganizada.

No obstante, hay que tener en cuenta que si en una tabla borramos muchas filas, pero inserta-
mos pocas el espacio libre que va quedando en las data pages no se recupera en su totalidad.
Esto puede causar pérdida de rendimiento y, por tanto, es aconsejable que reorganicemos las
2
tablas de vez en cuando .

2
No es el objetivo de esta conferencia presentar aspectos sintácticos, por tanto no abordare-
mos aquí en profundidad los mandatos y opciones que tenemos para realizar estas tareas. No
obstante, recomendamos al lector que revise en la ayuda de Transact SQL, el ítem DBCC, ya
que es con dicho mandato con el que habitualmente reorganizamos las estructura de una base
de datos.

9
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Consejos y trucos
Si queremos recuperar de forma automática el espacio que va quedando
en las páginas de datos conforme borramos filas dentro de una tabla, es
conveniente que dicha tabla tenga un índice clustered.

¿Qué es el Fill Factor?


El Fill Factor o factor de relleno indica cómo estarán las páginas de los índices cuando cre-
emos el índice. Un factor de relleno bajo hace que se creen más páginas de las que necesita-
ríamos para albergar el contenido de claves actuales. Esto facilitará la rapidez en futuras ac-
tualizaciones, pero penalizará las consultas actuales al tener que actualizar más páginas. Por
el contrario, un factor de relleno alto creará menos páginas lo que hará las consultas más rápi-
das, pero penalizará las inserciones posteriores. Este factor sólo se tiene en cuenta a la hora
de crear el índice, pero no con posterioridad durante el mantenimiento. En los índices clustered
el factor de relleno afecta a las páginas de datos. En los non-clustered sólo a las de índice.

Un factor de relleno con valor 0 indica que las páginas de hoja en los índices non-clustered o
las de datos en los clustered estarán totalmente llenas, pero que habrá espacio (al menos una
fila) en las intermedias para ampliar el índice sin demasiado coste. En cambio, un fill factor de
100 deja totalmente completas todas las páginas lo que lo hace adecuado para sistemas total-
mente estáticos. Un factor de relleno con valor de 50 nos da una situación de equilibrio en las
páginas que puede ser valorable como estándar para aplicaciones no críticas en cuanto tiempo
de respuesta ni en las inserciones ni en las queries.

Consejos y trucos
Si queremos un sistema muy rápido en la selección de datos, lo mejor es
usar un índice clustered con un factor de relleno de 100. Si embargo,
este será el sistema más lento posible en las actualizaciones.

En versiones anteriores de SQL Server el factor de relleno afecta sólo a las leaf pages o a las
data pages. A partir de la versión 6.5 también pueden verse afectadas las node pages y la root
page. Para ello hemos de usar la cláusula pad_index del mandato CREATE INDEX.

La labor que más enlentece la inserción de datos es la de la división de páginas que se produ-
ce para mantener de forma automatizada una estructura B-Tree. Como ya hemos dicho antes,
cuando las páginas se van llenando es necesario dividirlas en dos de forma que quede espacio
libre en ellas para albergar nuevos valores. Esta continua división de páginas puede ser total-
mente perjudicial para la rapidez en la actualización. Si esto se produce es conveniente que
revisemos nuestra estrategia de índices y la cambiemos por otra donde se produzcan menos
divisiones de páginas.

Consejos y trucos
La división de páginas de un índice es una operación que queda regis-
trada en el log de transacciones de cada base de datos. Si queremos
monitorizar el datos, podemos ejecutar la siguiente SELECT:
SELECT Count(*) from syslogs where op=16

Ya que 16 es el código de operación con el que se guardan este tipo de acciones


en el log.

10
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Como ejercicio recomendamos al lector que cargue la página de propiedades de un índice
(botón derecho del ratón sobre la tabla y elegir Indexes –claro dentro del SQL Enterprise Ma-
nager-) tal como puede verse en la figura 5 y modifique el Fill Factor que aparece en ella para
cualquier de sus índices. No es necesario que reconstruya el índice cada vez que cambie en
esta pantalla el Fill Factor ya que el sistema recalcula de forma inmediata el impacto que cada
cambio tendrá en el tamaño del índice, en los niveles del árbol y en las filas por cada tipo de
página. Verá consecuencias interesantes, como que si anota 0 ó 50 los datos reflejados serán
muy similares, pero si se fija en la diferencia entre 1 y 100 verá que es impactante. Un valor de
1 hace subir las cifras de filas por página de modo increíble, ya que esta es la fórmula con la
que más páginas casi vacías se crean. Un valor de 100 deja el tamaño superajustado, ya que
si lo usamos se crearán tantas páginas como necesitemos para dar cobertura a los valores
actuales sin desperdiciar ni una gota de espacio libre en las páginas del índice.

Figura 5: Modifique el Fill Factor y vea sus consecuencias

¿Cuándo usar un índice clustered?


En general podemos enunciar las siguientes normas a seguir para la optimización del uso de
índices en una base de datos:

1. En las aplicaciones transaccionales donde existan muchos procesos de actualización


de la base de datos hay que reducir al máximo el uso de índices.
2. En las aplicaciones cuyo carácter principal sea la consulta hay que poner el mayor nú-
mero de índices que faciliten la selección.
3. Si una tabla ha de tener un índice y sólo uno, es preferible que sea clustered. En este
caso las actualizaciones serán levemente más lentas, pero esto no será demasiado
notorio en contraste con la mucho mejor respuesta en las consultas.
4. Cuando la tabla ha de tener más de un índice, lo normal es que pongamos un clustered
en:
a. Las columnas donde existan más claves duplicadas

11
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
b. Las columnas donde se vayan a hacer selecciones entre un rango amplio
c. Las columnas sobre las que se van a hacer JOIN
d. Las columnas sobre las que se vaya a hacer ORDER BY

En general, todo este tipo de operaciones son más pesadas que las de seleccionar un
caso en una clave primaria, de forma que no siempre la elección de un índice clustered
para la clave primaria es la más acertada.

Consejos y trucos
En general, y a pesar de cualquier norma enunciada hasta ahora lo me-
jor es que para afinar totalmente un sistema probemos con él distintas
alternativas de indexación y finalmente seleccionemos la que nos parez-
ca más adecuada. Para esto SQL nos da la importante ventaja de que
las tablas pueden diseñarse prácticamente sin ningún índice, o con de-
terminados índices, pero que cualquiera de estos contextos es fácilmente
cambiable sin tocar el código fuente de las aplicaciones que corren con-
tra la base de datos de la que estudiamos sus índices.

La densidad de los índices


Por densidad de un índice entendemos una cifra tal que multiplicada por el número de filas de
una tabla nos dará la media de filas duplicadas por clave de la misma. La fórmula para calcular
la densidad es:

Densidad = 1/Valores únicos de la tabla

Imaginemos, por ejemplo, que tenemos una tabla de cabecera de facturas que tiene 5.000 filas,
cada una con un valor único que es el número de factura. Igualmente, tenemos otra tabla con
las líneas de la factura. Esta tabla tiene 52.000 filas. Esta segunda tabla tiene un índice por el
número de factura. Lógicamente, en dicho índice los valores únicos serán 5.000, ya que esas
son las facturas que existen. La densidad de ese índice será 1/5.000, o lo que es lo mismo
0.0002. Por tanto, la media de valores duplicados será: 52.000 * 0.0002 = 10.4, o lo que es lo
mismo, cada factura tiene una media de 10,4 líneas.

Saber la densidad de un índice puede interesarnos a fin de calcular lo pesada que será la res-
puesta de una consulta debido al excesivo número de claves duplicadas presentes en la mis-
ma.

Si deseamos conocer el dato de la densidad de nuestros índices junto con cualquier otro de
interés al respecto de los mismos podemos usar:

DBCC SHOW_STATISTICS (tabla, índice)

El Index Covering
Por Index Covering se conoce la técnica por la cual cuando en un índice non-clustered se pre-
gunta en la SELECT sólo por un campo que es parte del índice, la SELECT se resuelve sólo
con las leaf pages del índice sin llegar a las páginas de datos.

12
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Por ejemplo, pensemos en una tabla de alumnos que tiene un índice non-clustered por el nom-
bre del alumno y analicemos la siguiente SELECT:

SELECT nombre FROM alumnos


WHERE nombre LIKE ‘A%’ AND
Ciudad = ‘Madrid’

Esta consulta nos mostraría todos los alumnos cuyo nombre comienza por la letra A y que vi-
ven en Madrid. SQL Server habrá empleado para resolverla la técnica del Index Covering, ya
que el dato que se pide mostrar en la SELECT es sólo el nombre del alumno y dicho dato se
encuentra en las leaf pages del índice, por lo que no necesita ir a buscarlo a la tabla corres-
pondiente. Sin embargo si nuestra SELECT fuera:

SELECT nombre, ciudad FROM alumnos


WHERE nombre LIKE ‘A%’ AND
ciudad = ‘Madrid’

el asunto cambiaría notoriamente, ya que el dato de la ciudad no se encuentra en las Leaf Pa-
ges del índice y, por tanto, hay que ir a buscarlo a la tabla correspondiente.

Debido a esto es muy importante que siempre que sea posible usemos la técnica del Index
Covering, ya que hará mucho más rápidas nuestras consultas.

Consejos y trucos
Siempre que podamos es conveniente mostrar en la salida de la
SELECT el menor número de valores posible. Muchas veces empleamos
SELECT * sin que realmente necesitemos la totalidad de los valores de-
vueltos. Si restringimos lo devuelto por la SELECT a los valores presen-
tes en el índice non-clustered que se usará para resolverla, todo irá mu-
cho más rápido.

El optimizador de consultas
SQL Server posee una herramienta interna llamada el optimizador de consultas. Cada vez que
nosotros hacemos una SELECT dicha SELECT es analizada por el optimizador que es quien
decide finalmente los índices que se usarán para resolver la consulta en cuestión. El objetivo
perseguido por el optimizador es conseguir resolver la consulta con la mejor cifra de accesos
posibles.

El optimizador de consultas trabaja del siguiente modo:

1. Primero analiza y normaliza la sintaxis de la consulta y las referencias a objetos


2. A continuación optimiza la consulta y genera el plan de ejecución de la misma
3. Compila el plan generado
4. Ejecuta el plan y devuelve los resultados al usuario

13
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Dentro de estos pasos, lógicamente, el más importante es el de la optimización. Podemos divi-
dirlo en varias fases:

1. Análisis de la consulta

1. Encontrar los argumentos de búsqueda


2. Encontrar las cláusulas OR
3. Encontrar las JOINS

2. Selección de índices a usar

1. Elegir el mejor índice para los argumentos de búsqueda presentes


2. Elegir el mejor método para cubrir los OR
3. Elegir los mejores índices para las JOIN
4. Elegir los mejores índices para usar con cada tabla

3. Orden de proceso de las JOINS

1. Evaluar el orden de las JOINS


2. Calculara los costes
3. Evaluar otras opciones para resolver las JOINS

4. Selección del Plan

La página de distribución
En todo este proceso de optimización existe una herramientas muy importante para el optimi-
zador, se trata de lo que denominamos la página de distribución de cada índice. La página de
distribución es un recurso usado por el optimizador de consultas para conocer el número apro-
ximado de filas que concordarán con un argumento de búsqueda. Esta página es mantenida de
forma automática por el sistema, pero el administrador puede actualizarla a través de

Update Statistics

Hay que tener en cuenta que el optimizador sólo usa la página de distribución cuando en las
condiciones de búsqueda empleamos constantes. Por ejemplo, se usará con:

WHERE idcliente=5467

Y no se usará con

WHERE idcliente = @mivariable

La figura 6 nos muestra es aspecto de la información de la página de distribución para una


tabla de clientes y un índice sobre el código del mismo.

14
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
Figura 6: Una página de distribución

Usando esta página de distribución, el optimizador sabe que la resolución de un argumento de


búsqueda como:

WHERE idcliente >= 1000 and idcliente <= 7000

le supondrá leer del orden de 6.069 filas, ya que la fila 1.000 se encuentra en el paso 20 y la
fila 7.000 se encuentra en el paso 139. La diferencia entre ambos valores es 119. Dicha dife-
rencia multiplicada por el intervalo medio entre pasos (51) nos da la cifra mencionada.

La página de distribución es, pues, básica para el optimizador, ya que usándola puede saber el
número de filas previsto que devolverá la consulta.

El optimizador y los procedimientos almacenados


Cuando creamos un procedimiento almacenado, el sistema realiza el correspondiente plan
para la ejecución de las instrucciones del procedimiento en función de los parámetros usados.
Si, por ejemplo, añadimos un índice a la tabla el plan creado lo ignorará. Esto podemos evi-
tarlo usando la opción with recompile a la hora de crear el procedimiento almacenado.

Es muy importante que tengamos esto en cuenta, ya que la mayoría de las aplicaciones clien-
te/servidor están basadas en el uso intensivo de los procedimientos almacenados que luego
son ejecutados a requerimiento de la aplicación cliente. Si creamos nuestros procedimientos

15
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS
conteniendo SELECT con datos variables de cualquier tipo que luego son pasados en el mo-
mento de la ejecución, el plan para resolver la SELECT se creará con el primer juego de pará-
metros enviados al procedimiento y ya nunca se volverá a cambiar a no ser que usemos la
opción descrita.

Consejos y trucos
Si programamos procedimientos almacenados con SELECT que contie-
nen muchos datos variables que se pasan como parámetros en el proce-
dimiento, es conveniente usar la opción with recompile ya que si no el
plan generado por el optimizador estará basado en los parámetros usa-
dos la primera vez que se ejecute el procedimiento y no se volverá a
cambiar más. Esto puede ser nefasto para el rendimiento

Cuidado con lo empleado en WHERE


La rapidez o lentitud con la que se ejecute una consulta está fundamentalmente en función de
lo que aparece en la cláusula WHERE de la misma. Por esto es conveniente que seamos muy
cuidadosos con las condiciones de selección que ahí anotemos. Pongo un ejemplo, tenemos
un índice por una columna de tipo datetime y deseamos seleccionar las filas correspondiente al
mes de mayo. Veamos dos estilos distintos de escribir el WHERE:

WHERE Datepart(month,fecha) = 5

WHERE fecha >= ‘5/1/1997’ and fecha < ‘6/1/1997’

El primer estilo anulara el uso del índice, en cambio el segundo sí lo usará.

Debemos, por tanto, en nuestras WHERE evitar el uso de funciones que inhiban el uso de los
índices que ayudarán al optimizador a resolver la consulta de modo rápido.

Igual sucede con el uso de LIKE. Si usamos el siguiente patrón ‘A%’ se usará el índice, pero si
usamos ‘%A%’ no se usará, lógicamente, ya que en este segundo caso lo que pretendemos
localizar es una A en cualquier posición de la clave mientras que en el primero intentamos ha-
llar cualquier valor que comience por A. Lógicamente, las claves de índice en SQL Server
siempre comienzan a evaluarse comparando por los valores a la izquierda.

Conclusiones
En general no es cuestión de dar aquí un conglomerado enorme de consejos sobre qué usar
en las cláusulas WHERE u otro tipo de cosas similares. Quizá en otro momento y en otro lugar
amplie lo aquí escrito en esa dirección. Lo pretendido por esta conferencia es, más bien, acla-
rar cuestiones generales sobre la arquitectura de SQL Server, su funcionamiento y la repercu-
sión de determinados factores en aspectos de rendimiento.

En cualquier caso, aprovechando la versatilidad de los productos actuales de bases de datos


para cambiar sobre la marcha las estrategias de indexación y probar los resultados, lo que
mejor se le puede recomendar al lector es que pruebe y pruebe con distintas estrategias de
indexación y con distintas metodologías de acceso a los datos. Cuando el rendimiento es críti-
co para un sistema, ésta es una de las labores que más obligados a hacer nos veremos y que
más satisfechos nos dejará cuando apreciemos las notorias diferencias que unos leves cam-
bios han operado en nuestro sistema.

16
Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS