jueves, 24 de marzo de 2011

Cuando el desconocimiento cuesta.

En muchas situaciones me he encontrado ante clientes que manifiestan su alta preocupación porque la base de datos está "lenta". Lo primero que todo cliente debe conocer es que la palabra "lenta" para un administrador de base de datos, es una palabra que realmente no dice nada....Es cómo llevar un automovil al mecánico y decirle: "no está funcionando bien así que te lo dejaré y lo pasaré buscando más tarde", seguramente ud. ya sabe lo que pasará cuando esa persona recoja el automovil...Así que lo primero que se debe definir es cuanto es lento???, para ello ud debe reformularse su problema. Le daré un pequeño ejemplo, existe un reporte que se ejecuta al final del día el cual muestra el total de ventas del día de una sucursal de una tienda por departamentos, el mismo debe ejecutarse al cerrar las cajas de ventas (esto debe ser cuando se cierre la tienda para este ejemplo asumiremos a las 7pm), y todos los días existe una reunión de comité que evalúa las ventas de las sucursales a las 7:30 pm, ud ya debe intuir de que dicho reporte de ventas debe estar en las manos del gerente a mas tardar a las 7:10 pm, es decir tiene un máximo de 5 minutos para correr el mismo. En este caso la palabra "lento" deberia ser algo como esto: "El reporte debe ejecutarse en un máximo de 5 minutos, y está tardando 15 min"....suena diferente, cierto??.
Entonces ud debe definir como corporación muy bien su "meta" para poder comenzar a entonar, ese será nuestro primer paso....
Ahora bien una vez definida la meta, quiero conversarles a cerca de un problema muuuuyyyy común, en cuanto a rendimiento de una base de datos se refiere:
Cómo escribir SQL adecuados:
Probablemente al leer esa frase, venga a su mente como Administrador de base de datos que es algo como esto: "Esa no es su responsabilidad sino la del desarrollador", además el DBA en muchos casos "no puede tocar el código fuente", aunque inicialmente tal declaración parezca cierta dejeme informarle, que la primera persona a la que llamarán cuando los procesos tarden más de lo acordado en su ejecución, será a ud., y muy probablemente sus alegatos no sean comprendidos por el usuario final. Por lo que nuestro primer paso debe ser entender que todo código escrito en la base de datos finalmente debe ser nuestra responsabilidad...Ahora bien, como comente anteriormente un problema muy común es encontrarnos con SQL que necesitan ser re-escritos...Esto tiene mucha tela que cortar, así que iré al grano....Para ello comenzaré con nombrar algunos errores comunes al escribir sentencias SQL:
1) No utilizar variables bind (sobre esto ud hay mucha información escrita por el gurú Tom Kyte en su blog: http://asktom.oracle.com), por lo que no ahondaré mucho en este tema).
2) No utilizar querys escalares: los queries escalares son mejor conocidos como "Select de Select", sin embargo muchos desarrolladores ignoran el hecho de que se pueden ahorrar muchos JOIN los cuales son más costosos con el uso de estos queries escalares, y mejor aún los mismos pueden ser almacenados y ser reutilizados...un pequeño ejemplo, que lo ilustrará ahora:
Supongamos 2 tablas a las que llamaremos t1 y t2:
SQL> CREATE TABLE t10 ( a VARCHAR2(10), b VARCHAR2(10) );

Table created.

SQL> CREATE TABLE t20( b VARCHAR2(10), b_detalle VARCHAR2(100) );

Table created.

SQL> INSERT INTO t10 ( a, b ) VALUES ( 'A1', 'B1' );

1 row created.

SQL> INSERT INTO t10 ( a, b ) VALUES ( 'A1', 'B2' );

1 row created.
SQL> ed
Wrote file afiedt.buf

1* INSERT INTO t20 ( b, b_detalle ) VALUES ( 'B1', 'PARA PROBAR ESTOS VALORES' )
SQL> /

1 row created.

Ahora bien, supongamos que necesitamos una consulta, que nos muestre todo lo que esté en t10 y t20, por lo que es probable que este pensando en algo como esto:

SELECT t10.a, t10.b, t20.b_detalle
FROM
t10
LEFT OUTER JOIN t20 ON ( t20.b = t10.b );

Inicialmente esto puede parecer una buena idea, sin embargo a medida que los outer join se complican los costos son mayores, inspeccionemos el plan de ejecución de tal sentencia:
Execution Plan
----------------------------------------------------------
Plan hash value: 157079380

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 146 | 5 (20)| 00:00:01 |
|* 1| HASH JOIN OUTER | | 2 | 146 | 5 (20)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T10 | 2 | 28 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T20 | 1 | 59 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - access("T20"."B"(+)="T10"."B")

Por favor note el costo en CPU que ocasiona el OUTER JOIN, ahora veremos que ocurre cuando utilizamos queries escalares:
Misma consulta re-escrita:

SELECT t10.a, t10.b, ( SELECT t20.b_detalle FROM t20 WHERE t20.b = t10.b ) b_detalle
FROM t10;
Inspeccionemos su plan de ejecución:
Execution Plan
----------------------------------------------------------
Plan hash value: 1399811295

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 28 | 2 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T20| 1 | 59 | 2 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T10 | 2 | 28 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("T20"."B"=:B1)

Note
-----
- dynamic sampling used for this statement (level=2)

Por favor note como el costo en CPU es mucho menor, inicialmente ud pensará que no es muy grande la mejora, pero espere, recuerde 2 cosas:
1) Esto no es un ambiente de producción por lo que el outer join es muy sencillo, pero qué pasaría si lo complicamos más, el costo sería muchisimo mayor....
2) En las sentencias SQL, no debe ser solo cuestion de cuanto tiempo tarda una consulta en ejecutarse, sino de recursos y escalabilidad, además tenga en cuenta que todo suma; por ejemplo que sucedería si la misma consulta la ejecutan 500 usuarios de forma simultánea en una 1 hora determinada, seguro se asustará cuando vea los resultados, en todo caso, con esto me basta para mostrarle a ud, cuanto lo puede ayudar conocer un poco más a cerca de como escribir sus SQL.

Espero les haya sido útil, les mostraré más en futuras entregas...
Saludos Cordiales

No hay comentarios:

Publicar un comentario