Sei sulla pagina 1di 11

--------------------------------------------------------------------TRABAJO DE DISEO DE BASE DE DATOS DE NOMINA - Crear tablas de Empleado, Tipo_rol, cab_rol, det_rol, descuento, - Ingresar informacin en todas

las tablas - Aplicar lo indicado en la lectura (cdigo, estadsticas, etc.) - Mostrar resultados y concluir

--------------------------------------------------------------------Mejorando el rendimiento de una base de datos a travs e la desnormalizacin y la utilizacin adecuada de ndices.
Por Adrin D. Garca T

Introduccin

El proceso de normalizar una base de datos es lo que nos garantiza una serie de propiedades tales como la no redundancia de datos y la simplificacin y optimizacin del rendimiento del motor para operaciones transaccionales (agregar, eliminar, modificar y buscar una fila utilizando su clave primaria).

Pero en algunos casos veremos que muchas veces el rendimiento no se lleva de la mano con la normalizacin de las bases de datos para operaciones de consultas. El ejemplo tpico es el de querer obtener un total y este depende de la suma de una o varias columnas definidas en una serie de filas. Si resulta ser que esta operacin se ejecuta lo suficientemente seguido se obtendra un mejor rendimiento si ya tuvisemos ese total precalculado. Pero muchas veces las opciones de que desnormalizar no son tan obvias como la antedicha y generalmente son las que mayores beneficios nos brindan, como el caso que analizaremos en este artculo.

Hay casos en donde la desnormalizacin sumada a la utilizacin inteligente de los ndices nos dan una mejora del rendimiento en varios ordenes de magnitud. Para ejemplificar esto tomemos un caso de ejemplo:

El caso de ejemplo

El caso de ejemplo que utilizaremos es el de una base de datos de un sistema contable. En este sistema es necesario obtener el saldo de una cuenta para una fecha determinada. Esto significa sumar todos los movimientos que tiene esa cuenta desde el primer movimiento ingresado hasta la fecha pedida inclusive. He aqu el diagrama (reducido) de la base de datos:

Diagrama de base de datos

La tabla CUENTA en este caso contiene aproximadamente unas 1000 filas, all se encuentran las definiciones de las cuentas contables. La tabla ASIENTO tiene un crecimiento de unas 100.000 filas anuales. Un asiento involucra a varios movimientos que se encuentran en la tabla de detalle llamada ASIENTO_DETALLE que tiene un crecimiento de unas 600.000 filas anuales. Como podemos ver en el diagrama, las tablas estn normalizadas a la 3era forma normal.

El problema Para calcular el saldo de una cuenta a una fecha determinada hay que realizar un JOIN de la tabla ASIENTO, all se encuentra la fecha, con la tabla de ASIENTO_DETALLE, donde estn los valores a sumar y luego un JOIN entre la tabla ASIENTO_DETALLE y CUENTA para obtener el nombre de la misma. La sentencia que calcula el saldo es la siguiente:

SELECT

CUENTA.CTA_codigo, CUENTA.CTA_nombre, SUM(ASIENTO_DETALLE.ASD_debe) AS DEBE,

SUM(ASIENTO_DETALLE.ASD_haber) AS HABER

FROM CUENTA

INNER JOIN ASIENTO_DETALLE ON

CUENTA.CTA_codigo = ASIENTO_DETALLE.CTA_codigo

INNER JOIN ASIENTO ON

ASIENTO_DETALLE.ASI_codigo = ASIENTO.ASI_codigo

WHERE ASIENTO.ASI_fecha <= @fecha AND

CUENTA.CTA_codigo = @cuenta

GROUP BY CUENTA.CTA_codigo, CUENTA.CTA_nombre


En donde @fecha y @cuenta son los parmetros que recibe para resolver la bsqueda.

El problema es que al ao y medio de estar operando el sistema acumulo aproximadamente 1.000.000 de filas en la tabla ASIENTO_DETALLE y este clculo se torna lento. Dado que adems este clculo se debe poder realizar para cualquier fecha determinada no podemos sumarizar los totales y tenerlos ya listos para su consulta.

La ejecucin de esta sentencia tarda aproximadamente 2 minutos en un servidor pequeo de test y desarrollo con un lote de pruebas de 1 ao luego de haber ejecutado la instruccin DBCC DROPCLEANBUFFERS para que no exista ninguna pgina de datos en memoria.

Las estadsticas de entrada/salida generadas son las siguientes:

Table 'ASIENTO_DETALLE'. Scan count 1, logical reads 10205, physical reads 3, read-ahead reads 10237.

Table 'ASIENTO'. Scan count 1, logical reads 519, physical reads 1, read-ahead reads 519. Table 'CUENTA'. Scan count 1, logical reads 2, physical reads 2, read-ahead reads 0.
Y el plan de ejecucin grfico es el siguiente:

Analizando este ltimo grafico vemos que el motor realiza un cluster-index scan sobre la tabla ASIENTO (100.000 filas), trayendo todas las filas de esta tabla ya que pedimos un saldo a la fecha, y esa misma operacin sobre la tabla de ASIENTO_DETALLE (600.000 filas). La operacin de cluster-index scan va leyendo cada fila de la tabla, es similar a un table scan (recorrer la tabla de punta a punta) .

De estas 2 operaciones es donde las estadsticas de entrada y salida nos indican que fueron necesarios 10205 lecturas de pginas para la tabla de ASIENTO_DETALLE y 519 para la tabla ASIENTO.

Solucin mediante ndices Una solucin a este problema sera crear ndices que sean ms propicios para resolver la consulta. Una forma rpida de averiguar si estamos utilizando los ndices correctos es ejecutar la herramienta "Index Tunning Wizard" mediante en Analizador de Queries y ver que ndices propone. Es nuestro caso la propuesta ofrecida por la herramienta es la de crear los siguientes ndices en la tabla ASIENTO_DETALLE y ASIENTO.

CREATE NONCLUSTERED INDEX [ASIENTO_DETALLE4] ON [dbo].[ASIENTO_DETALLE] ([CTA_codigo] ASC )

CREATE NONCLUSTERED INDEX [ASIENTO5] ON [dbo].[ASIENTO] ([ASI_codigo] ASC, [ASI_fecha] ASC )


Ejecutando nuevamente la consulta con estos nuevos ndices obtenemos las siguientes estadsticas:

Table 'ASIENTO_DETALLE'. Scan count 1, logical reads 2990, physical reads 2, read-ahead reads 304. Table 'ASIENTO'. Scan count 1, logical reads 223, physical reads 1, read-ahead reads 223. Table 'CUENTA'. Scan count 1, logical reads 2, physical reads 2, read-ahead reads 0.
Podemos apreciar una mejora sustancial en la cantidad de lecturas sobre las tablas ASIENTO_DETALLE y ASIENTO. En el mismo servidor y bajo las mismas condiciones que la prueba anterior el tiempo de ejecucin del mismo query fue de 7 segundos. Este es el nuevo plan de ejecucin que obtuvimos:

Vemos que con los nuevos ndices ahora el motor slo trae las filas pertenecientes a la cuenta que estamos buscando de la tabla ASIENTO_DETALLE pero an el motor trae las 100.000 filas de la tabla ASIENTO para poder filtrar por fecha. En este punto podemos decidir si con este nivel de rendimiento nos sentimos conformes o decidimos seguir adelante.

En busca del rendimiento extremo: desnormalizacin Vamos a seguir optimizando el rendimiento pero, por donde buscar? Ya definimos ndices que en teora son ptimos. Hay espacio para seguir optimizando an ms?.

La respuesta es s y es desnormalizando el diseo lgico. Realizando un anlisis profundo de la consulta vemos que de la tabla ASIENTO slo necesitamos la columna ASI_fecha. Que pasara si ubicamos el valor de esta columna en las filas de la tabla ASIENTO_DETALLE?: ya no necesitaramos realizar el join de esta tabla. Que implicara desde el punto de vista de nuestra aplicacin? Slo agregar una columna mas a la tabla ASIENTO_DETALLE y darle valores en el momento de insercin. No requerira de nuestra parte mantener disparadores (triggers) o procedimientos almacenados complejos para mantener el valor de esta columna en sincrona con los de su cabecera, como sera el caso de una columna totalizadora.

Aplicando lo antedicho, el diseo lgico de nuestra base de datos quedara as:

Nuevo diseo lgico con la desnormalizacin aplicada. La columna resalta es la que se agrego. Pero para aprovechar esta nueva columna tendramos que definir un ndice que la contenga y para aprovechar al mximo las capacidades de SQL Server tenemos a nuestra disposicin los ndices del tipo Agrupado (Cluster). Este tipo de ndice tiene la particularidad de que afecta al orden fsico de las filas dentro de la tabla: el orden de las filas sigue el orden definido en el ndice. Esto hace que sean extremadamente eficientes en la bsqueda de rangos de claves: una vez ubicada la primera fila que cumpla con la condicin de bsqueda el motor solo debe realizar lecturas secunciales de filas que estn en la misma pgina de datos o en pginas continuas (si la tabla est desfragmentada en la base de datos). Cada vez que definimos una clave primaria a una tabla, a menos que definamos lo contrario, el motor crea un ndice agrupado (cluster) sobre la tabla. Es decir, que bsicamente todas nuestras tablas estn ordenadas fsicamente en la base de datos por la clave primaria. En la mayora de los casos esto es lo deseado pero como veremos, en nuestro caso obtendramos un mejor rendimiento si creramos un ndice del tipo agrupado (cluster) que incluya a la columna ASI_fecha . Para ello aplicamos el siguiente script de modificacin de ndices.

ALTER TABLE dbo.ASIENTO_DETALLE

DROP CONSTRAINT PK_ASIENTO_DETALLE GO

DROP INDEX dbo.ASIENTO_DETALLE.ASIENTO_DETALLE4

GO

CREATE CLUSTERED INDEX IX_ASIENTO_DETALLE_FECHA ON dbo.ASIENTO_DETALLE

( CTA_codigo, ASI_FECHA ) ON [PRIMARY]

GO

ALTER TABLE dbo.ASIENTO_DETALLE ADD CONSTRAINT

PK_ASIENTO_DETALLE PRIMARY KEY NONCLUSTERED

( ASI_codigo , ASD_item ) ON [PRIMARY]

GO

Como podemos ver hemos creado un ndice agrupado (cluster) sobre las columnas CTA_codigo y ASI_fecha que corresponden a los parmetros de bsqueda y luego volvimos a redefinir la clave primaria como no agrupada (nonclustered). Ejecutando nuevamente la misma consulta obtenemos ahora las siguientes estadsticas de entrada y salida:

Table 'ASIENTO'. Scan count 524, logical reads 1576, physical reads 1, read-ahead reads 203.

Table 'ASIENTO_DETALLE'. Scan count 1, logical reads 8, physical reads 2, read-ahead reads 7. Table 'CUENTA'. Scan count 1, logical reads 2, physical reads 2, read-ahead reads 0.
Y el siguiente plan de ejecucin:

Podemos apreciar una mejora sustancial en el tratamiento de la tabla ASIENTO_DETALLE, ahora solo realizamos 8 lecturas de pginas en vez de las 2990 antes de la desnormalizacin pero en cambio se han incrementado la cantidad de lecturas sobre la tabla ASIENTO.

Pero no era que despus de la desnormalizacin el motor no necesitara realizar el JOIN sobre la tabla ASIENTO?. Si, es verdad pero para ello debemos modificar la consulta para que en la misma no haga referencia a esta tabla. Si la revisamos veremos que an hacemos referencia a la columna ASI_fecha de la tabla ASIENTO y no a esa misma columna pero de la tabla ASIENTO_DETALLE. Si modificamos la consulta a:

SELECT CUENTA.CTA_codigo, CUENTA.CTA_nombre, SUM(ASIENTO_DETALLE.ASD_debe) AS DEBE,

SUM(ASIENTO_DETALLE.ASD_haber) AS HABER FROM CUENTA INNER JOIN ASIENTO_DETALLE ON

CUENTA.CTA_codigo = ASIENTO_DETALLE.CTA_codigo INNER JOIN ASIENTO ON ASIENTO_DETALLE.ASI_codigo = ASIENTO.ASI_codigo WHERE ASIENTO_DETALLE.ASI_fecha <= @fecha AND CUENTA.CTA_codigo = @cuenta GROUP BY CUENTA.CTA_codigo, CUENTA.CTA_nombre
Y la ejecutamos nuevamente obtendremos los siguientes resultados a nivel de estadsticas:

Table 'CUENTA'. Scan count 1, logical reads 2, physical reads 2, readahead reads 0. Table 'ASIENTO_DETALLE'. Scan count 1, logical reads 8, physical reads 2, read-ahead reads 7.
Slo 8 lecturas sobre la tabla ASIENTO_DETALLE y ninguna sobre la tabla ASIENTO!. Ya el motor no utiliza ms esta tabla y el acceso a la tabla ASIENTO_DETALLE esta optimizado a su mxima expresin. Un ultimo detalle que nos ha quedado pendiente es remover el JOIN entre la tablas ASIENTO y ASIENTO_DETALLE de la consulta ya que no es ms necesario pero este cambio no afecta a la ejecucin de la consulta. El nuevo plan de ejecucin grfico es el siguiente:

El mismo refleja en su simplicidad los valores obtenidos en las estadsticas. Por ltimo el tiempo de

ejecucin de la consulta bajo las mismas condiciones anteriores se encuentra ahora por debajo del segundo (158 milisegundos para ser exactos).

Que hemos perdido durante el camino? Esta mejora de rendimiento espectacular de pasar de utilizar slo ndices ptimos en comparacin con la desnormalizacin y el uso de ndices cluster tiene los siguientes costos:

1) Al tener desnormalizado la columna fecha debemos crear mecanismos adicionales para mantener los valores en sincrona entre la cabecera y detalle. Este detalle puede "perderse" en algn momento durante la etapa de mantenimiento de la aplicacin con los consecuentes errores que puede producir. 2) Al tener un ndice cluster sobre una clave que no es primaria en la tabla de ASIENTO_DETALLE toda consulta que se realice a las filas utilizando la clave primara ser ahora ms lenta ya que SQL Server realizar una doble bsqueda. Esto significa que para traer todas las filas correspondientes a 1 asiento dado en la tabla normalizada slo se deben realizar 3 lecturas fsicas contra 10 lecturas fsicas en la tabla desnormalizada (clculo basado en obtener las 6 flas correspondiente a un asiento dado). Estas mismas estadsticas tambin se trasladan a las inserciones, eliminaciones ya actualizaciones.

Comparacin de los resultados La siguiente tabla resume la comparacin entre la opciones: (a) de slo buscar un mejor rendimiento agregando ndices y (b) la desnormalizacin con la reasignacin del ndice cluster

Tabla ASIENTO (a) ASIENTO (b) ASIENTO_DETALLE (a) ASIENTO_DETALLE (b) CUENTA (a) CUENTA (b) Opciones Opcin (a) Opcin (b)

Recorridas Lecturas lgicas Lecturas Fsicas sobre la tabla 1 223 1 0 0 0 1 2990 2 1 8 2 1 2 2 1 2 2 Tiempos de ejecucin 6 segundos 157 milisegundos

Lecturas hacia adelante 223 0 304 7 0 0

Conclusin Podemos ir ms all de las herramientas automticas que nos provee la herramienta y encontrar opciones que normalmente estn ocultas utilizando mecanismos como el desarrollado en este artculo para obtener una mejora en el rendimiento de forma espectacular.

El determinar que desnormalizamos y que no, no puede ser hecho utilizando una herramienta y si sumamos a esto el conocimiento tanto de la problemtica funcional como de las caractersticas propias del

SQL Server podemos romper barreras que nos parecen infranqueables y obtener mejoras de rendimiento que ni siquiera pueden ser logradas con slo comprar un servidor ms potente.

Tambin debemos tener en cuenta las perdidas que asumimos para obtener este rendimiento y es el eterno dilema entre las operaciones transaccionales y las operaciones de consulta. Si mejoramos el rendimiento en un sentido lo desmejoraremos en el otro. Siempre hay que hacer una evaluacin de perdidas y ganancias antes de consolidar cualquier cambio que realicemos en el diseo y configuracin de las bases de datos.

Adrian D. Garcia MCSD

NDSoft - Consultora y Desarrollo

Adrin D. Garca se desempea como consultor en NDSoft especializado en SQL Server y tecnologas de desarrollo .NET en aplicaciones empresariales. Desde el ao 1993 se especializo en SQL Server y desarrollo de aplicaciones con tecnologas Microsoft. Fue nombrado MSDN Regional Director de Argentina entre los aos 1997 y 1999, y durante el ao 2002 fue Director de la comunidad online de C# de MSDN Latinoamrica

Potrebbero piacerti anche