Usando Servidor y embedded en la misma aplicación

Deja un comentario

Si queremos que nuestras aplicaciones “vuelen”, o sea que se ejecuten a gran velocidad podemos usar la técnica que se explica en este artículo. El autor de este blog usa esa técnica desde hace muchos años con gran suceso.

Entendiendo la situación:

El ancho de banda de todas las redes es limitado, en algunos casos puede ser muy grande pero siempre es limitado. Si demasiados datos transitan por la red ésta se vuelve lenta. En general las aplicaciones de bases de datos no saturan la red salvo que haya demasiados usuarios conectados y demasiadas consultas se estén realizando al mismo tiempo.

Sin embargo, la realidad es que muchas veces la misma red que se usa para las aplicaciones de bases de datos también se usa con otros objetivos, por ejemplo para conectarse a Internet. Y en casos así es frecuente que alguien (aunque no debería hacerlo) esté descargando fotografías, canciones, vídeos o películas.

Ante ese panorama no habrá red que aguante, hagamos lo que hagamos para aumentar la velocidad de nuestras consultas siempre serán lentas porque la red estará saturada.

Entonces, el primer y elemental paso es conseguir que la red se use única y exclusivamente para bases de datos y para nada más, por ningún motivo. Si necesitan una red para Internet, que instalen otra, pero la que se usa para bases de datos debe usarse solamente y exclusivamente para bases de datos. Eso es innegociable.

Pero aún teniendo una red exclusiva algunas tareas de los usuarios pueden ser lentas. Veamos:

Una empresa tiene 5.000 clientes, cuando se va a realizar una venta la aplicación muestra en una grilla los nombres de esos clientes. El usuario elige a unos de ellos.

¿Qué sucedió?

Que la consulta del usuario involucró que por la red transiten los datos de esos 5.000 clientes. Si es un solo usuario quien está vendiendo quizás no sea mucho problema pero ¿y si son 10, 40, 80 o más usuarios?

Allí ya es otro tema, porque ya muchos datos están transitando constantemente por la red.

Entonces, lo que necesitamos es una técnica que nos permita obtener los datos de esos 5.000 clientes pero sin usar la red. ¿Es eso posible?

La solución:

Ya hemos visto que si demasiados datos transitan por una red ésta se vuelve lenta, entonces lo que debemos conseguir es que sean muy pocos los datos que transiten por ella para que cuando lo hagan sea a gran velocidad.

Y eso obtendremos si usamos una Base de Datos auxiliar.

En esta Base de Datos auxiliar guardaremos las tablas de lookup y las tablas maestras. ¿Qué son las tablas de lookup? Las que guardan datos que se insertan una sola vez y luego se usan muchas veces, pero nunca se modifican. Por ejemplo: nombres de localidades, nombres de los tipos de documentos, nombres de los tipos de envases, nombres de las unidades de medida, nombres de las marcas de productos, nombres de sucursales, nombres de las monedas extranjeras, nombres de Bancos, nombres de vendedores, nombres de cobradores, nombres de países, etc. En esas tablas se insertan filas pero nunca se las modifica. Bueno, la excepción sería si se escribió mal el nombre, pero solamente eso, nada más se suele cambiar.

Y en las tablas maestras se guardan los datos de clientes, proveedores, productos, etc.

Tanto las filas de las tablas de lookup como las filas de las tablas maestras se usan principalmente para consultas. Y para asegurarnos que en las otras tablas se ingresen datos existentes. Y eso normalmente se realiza a través de restricciones FOREIGN KEY.

Así, si por ejemplo estamos vendiendo en moneda extranjera estaremos seguros que esa moneda extranjera existe, porque su identificador y su nombre lo extraemos de la tabla de MONEDAS. Y en nuestra tabla de VENTAS nunca tendremos una moneda extranjera inexistente porque una restricción FOREIGN KEY lo impedirá.

Entonces, si queremos que nuestras aplicaciones muestren los datos a gran velocidad lo que debemos hacer es usar una Base de Datos local, no una Base de Datos externa.

La idea es la siguiente:

  • Creamos una Base de Datos auxiliar, que se guardará en el disco duro local y a la cual se conectará mediante embedded
  • En nuestra Base de Datos auxiliar guardamos las tablas de lookup y los datos de consulta de las tablas maestras (típicamente identificador, código, nombre, y a veces algunas columnas más) que se encuentran en el Servidor
  • Cuando un usuario quiere consultar una tabla de lookup o una tabla maestra, verificamos que la cantidad de filas de la tabla auxiliar coincida con la cantidad de filas de la tabla en el Servidor. Si no coincide, a la tabla auxiliar le insertamos las filas faltantes
  • Le mostramos al usuario la tabla auxiliar
  • Cuando el usuario eligió una fila de la tabla auxiliar, consultamos (si hace falta) la tabla remota para obtener los otros datos que necesitamos

Ejemplo:

  • Creamos una Base de Datos llamada AUXILIAR.FDB
  • Siempre nos conectaremos a ella usando embedded
  • A esa Base de Datos le agregamos una tabla llamada CLIENTESAUX con las columnas Identificador, Código, Nombre
  • Cuando el usuario necesita consultar los datos de un cliente, verificamos que el último Identificador de la tabla CLIENTESAUX sea igual al último Identificador de la tabla CLIENTES (que es la tabla que tenemos en el Servidor)
  • Si el Identificador de la tabla CLIENTES es mayor que el Identificador de la tabla CLIENTESAUX, le insertamos a la tabla CLIENTESAUX las filas faltantes
  • Le mostramos al usuario el contenido de la tabla CLIENTESAUX
  • Después que el usuario eligió una fila de CLIENTESAUX extraemos de la tabla CLIENTES que se encuentra en el Servidor los demás datos que necesitamos (su teléfono, su e-mail, etc.)

Ventajas:

  • El usuario consulta una tabla que se encuentra en el disco duro de su computadora, eso siempre es mucho más rápido que consultar una tabla que se encuentra en el disco duro de otra computadora
  • La red se usa al mínimo porque si hay 5.000 clientes transitaron por la red los datos de un solo cliente, no los datos de 5.000 clientes

Desventaja:

  • Se complica la programación de nuestra aplicación porque debemos escribir rutinas que se encarguen de actualizar las tablas auxiliares

Importante:

Con esta técnica se consiguen tiempos de respuesta buenísimos pero hay que tener en cuenta que la aplicación debe conectarse a una sola Base de Datos en el Servidor, ya que si primero se conecta a la Base de Datos “A”, luego a la Base de Datos “B”, luego a la Base de Datos “C”, si hay una sola Base de Datos AUXILIAR.FDB habrá que borrar los datos de cada una de sus tablas cada vez que se cambia de Bases de Datos y luego insertar las filas correspondientes. Allí ya se vuelve ineficiente. La solución en ese caso es tener una Base de Datos auxiliar para cada Base de Datos en el Servidor. O sea AUXILIAR_A.FDB, AUXILIAR_B.FDB, AUXILIAR_C.FDB, etc.

Conclusión:

Si creamos una Base de Datos auxiliar a la cual nos conectamos mediante embedded y guardamos en ella las tablas de lookup y las tablas maestras cuando el usuario haga una consulta obtendrá los datos a gran velocidad porque serán leídos desde su disco duro local lo cual siempre es muchísimo más rápido que leerlos desde un disco duro remoto.

La desventaja es que nuestra programación de la aplicación se complicará porque cada vez que el usuario quiera consultar una tabla de lookup o una tabla maestra deberemos verificar que la tabla auxiliar se encuentre actualizada y si no lo está entonces deberemos actualizarla. Siempre la tabla auxiliar deberá estar actualizada antes de mostrarle los datos que pidió.

Artículos relacionados:

¿Es seguro usar Servidor y embedded al mismo tiempo?

El índice del blog Firebird21

El foro del blog Firebird21

Anuncios

¿Por qué deben escribirse primero los INNER JOIN?

Deja un comentario

En el artículo:

Poniendo los JOIN en el orden correcto

ya había mostrado que los INNER JOIN siempre deben escribirse antes que los OUTER JOIN pero no había explicado claramente el motivo del por qué deba ser así.

Me explayaré más sobre ese tema ahora.

Si tenemos una consulta como la siguiente:

Listado 1

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla1
JOIN
   MiTabla2
      ON MiCondición1
JOIN
   MiTabla3
      ON MiCondición2

El optimizador del Firebird puede cambiar el orden de las tablas pues como se trata de un INNER JOIN todas las filas deben estar emparejadas y por lo tanto no importa cual se coloque primero. O sea que si después de la cláusula FROM ponemos a MiTabla1 o la ponemos a MiTabla2 o la ponemos a MiTabla3 el resultado no variará, será idéntico. Así mismo, en el primer o en el segundo JOIN podemos poner a MiTabla1, a MiTabla2, o a MiTabla3, y siempre obtendremos lo mismo.

Sin embargo, aunque el resultado final será el mismo, la eficiencia no será la misma. El optimizador del Firebird realiza su tarea mirando (entre otras cosas) la selectividad de los índices y el tiempo que demorarán. Puede quizás descubrir que para obtener la máxima eficiencia después de la cláusula FROM debe colocar a MiTabla2 y que el primer JOIN debe ser con MiTabla3.

Entonces, si lo considera necesario el optimizador puede tranquilamente poner a otra tabla después del FROM o cambiar el orden de los JOIN, no hay problema con eso porque el resultado obtenido será exactamente el mismo.

En síntesis, el optimizador intentará optimizar la consulta.

¿Y qué ocurre cuándo escribimos un LEFT JOIN?

Listado 2

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla1
LEFT JOIN
   MiTabla2
      ON MiCondición1

Listado 3

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla2
LEFT JOIN
   MiTabla1
      ON MiCondición1

Lo que ocurre es que los resultados devueltos por Listado 2 y por Listado 3 ya no serán idénticos si alguna de las columnas escritas después de la clásula SELECT puede ser NULL o si (este es el caso más común) la cantidad de filas de MiTabla1 no es igual a la cantidad de filas de MiTabla2.

Ejemplo: Si MiTabla1 tiene 5 filas y MiTabla2 tiene 20 filas, el resultado del SELECT puede tener 5 filas (si después de la cláusula FROM escribimos MiTabla1) o puede tener 20 filas (si después de la cláusula FROM escribimos MiTabla2). Evidentemente, son muy diferentes esos resultados.

En el Listado 1 no importaba cual tabla se escribía en el JOIN porque como todas las filas debían estar emparejadas daba lo mismo poner a una tabla o a la otra. Sin embargo, en el Listado 2 y en el Listado 3 eso ya no es verdad, aquí sí importa cual tabla se escribe en el LEFT JOIN y los resultados obtenidos pueden ser muy distintos en cada caso.

Conclusión:

Siempre que ejecutamos un SELECT el optimizador del Firebird intenta que ese SELECT muestre los resultados lo más rápidamente posible, para ello mira entre otras cosas la selectividad de los índices. Cuando usamos INNER JOIN puede cambiar el orden de las tablas porque eso no importa, ya que el resultado final será el mismo y si descubre que cambiando el orden de las tablas los resultados se mostrarán más rápido, entonces cambia el orden de las tablas. Pero cuando usamos LEFT JOIN ya no puede cambiar el orden de las tablas porque en la gran mayoría de los casos los resultados serán distintos. Entonces, deja de optimizar, allí mismo termina su tarea. La primera vez que encuentra un LEFT JOIN, RIGHT JOIN, o FULL JOIN, finaliza la optimización. Y es lógico, porque ya nada puede mejorarse.

Entonces, si nuestro SELECT conteniendo un OUTER JOIN (o sea, LEFT JOIN, RIGHT JOIN, o FULL JOIN) estaba optimizado, todo bien. Si no estaba optimizado, todo mal, ya que el optimizador del Firebird no lo mejorará.

¿Corolario?

Siempre, pero siempre, en todos los casos, debemos escribir primero los INNER JOIN y solamente después de ellos escribir los OUTER JOIN. De esa manera nos aseguraremos de que el optimizador del Firebird haga su trabajo y mejore si es posible la velocidad con la cual se obtienen los resultados.

Artículos relacionados:

Poniendo los JOIN en el orden correcto

El índice del blog Firebird21

El foro del blog Firebird21

Usando dos servidores para aumentar la velocidad de las operaciones

2 comentarios

Generalmente los usuarios quieren velocidad, quieren que las operaciones que realizan en la Base de Datos (SELECT, INSERT, UPDATE, DELETE) sean muy rápidas.

Una técnica que podemos emplear para aumentar la velocidad (además de la optimización de las consultas, que ya hemos visto en otros artículos de este blog) es tener en la misma computadora dos (o más) servidores instalados.

Como seguramente sabes, Firebird viene en tres arquitecturas: Classic, SuperClassic y SuperServer.

El problema con SuperServer es que mantiene un caché para todas las conexiones. O sea que si una Base de Datos tiene 40 conexiones, hay un solo caché para las 40 conexiones. Y si alguien está ejecutando una transacción o una consulta muy larga eso crea un “cuello de botella” para todos los demás usuarios, haciendo que sus operaciones se vuelvan muy lentas. Pero la gran ventaja de usar un solo caché es que si el usuario 2 quiere hacer la misma consulta que ya hizo el usuario 1 los datos de esa consulta ya muy probablemente se encuentran en el caché y por lo tanto no deben ser leídos del disco duro, proporcionando una gran velocidad (la memoria RAM es miles de veces más rápida que la memoria secundaria).

Como ves, usar SuperServer o usar Classic tiene sus ventajas y sus desventajas.

Entonces, ¿cuál es la mejor solución?

Usar dos servidores.

Un Servidor con la arquitectura SuperServer se usará para el mantenimiento de los datos de la Base de Datos y para las consultas rápidas. Un Servidor con arquitectura Classic se usará para las consultas lentas (que son lentas porque deben procesar muchos datos, no porque están mal diseñadas que ese es otro tema).

Para que esta técnica funcione sin problemas un solo Servidor será el encargado de las operaciones de mantenimiento de los datos (INSERT, UPDATE, DELETE). ¿Por qué? porque si los dos servidores pueden hacerlo podrían ocurrir conflictos y corromperse la Base de Datos. Por lo tanto hay que evitar esa posibilidad.

Entonces:

  • SuperServer se encargará de las operaciones de INSERT, UPDATE, DELETE y SELECTs rápidos
  • Classic se encargará de los SELECTs lentos

Como Classic usa un caché por cada conexión si un usuario está realizando una consulta muy lenta eso no les afectará a los demás usuarios.

¿Entonces, qué conseguimos con esto?

Que todos estén felices.

Los gerentes y los propietarios de las empresas generalmente se conectan a las bases de datos para realizar consultas. Entonces ellos siempre usarán Classic.

Los operadores se encargan de introducir datos y de imprimir informes de comprobación. Para la introducción de datos y para la impresión de informes cortos, usarán SuperServer; para la impresión de los informes que tendrán muchas páginas o que requieren de mucho procesamiento, usarán Classic.

Artículos relacionados:

Modelos de ejecución del Firebird y sus diferencias

Comparando las arquitecturas

Diferencias entre SuperServer, Classic y SuperClassic

El índice del blog Firebird21

SQL_RENDIMIENTO. Verificando la velocidad de las operaciones

4 comentarios

Muchas veces nos interesa saber que tan rápidas son las operaciones INSERT, UPDATE, DELETE, SELECT en una computadora Cliente o mismo en el Servidor del Firebird.

Esa información puede ser muy útil para recomendar la compra de más memoria RAM, un mejor procesador, un router de mejor calidad, etc.

La mayoría de las empresas disponen de varias computadoras y las velocidades con las cuales se realizan las operaciones en ellas no son las mismas, así que puede resultarnos muy provechoso poder determinar cuales son las computadoras lentas. Esa lentitud puede estar en el Servidor, en el Cliente, o en ambos.

Por ese motivo hice un programa que me ayudará con esa tarea y ahora lo comparto con los lectores de este blog. El enlace para descargarlo es:

http://www.mediafire.com/download/eudns020fs6xf64/SQL_RENDIMIENTO.ZIP

Está desarrollado en Visual FoxPro y tiene todos los programas fuente incluidos para que quienes conozcan dicho lenguaje puedan realizarle todas las modificaciones que crean pertinentes. También podría servirles como base para crear otros programas similares.

SQL_RENDIMIENTO1

Captura 1. Si haces clic en la imagen la verás más grande

Como puedes ver en la Captura 1, es bastante auto-explicativo pero si tienes alguna duda puedes preguntarme.

Para usarlo:

  1. Copia la Base de Datos de nombre VELOCIDAD.FDB en un disco duro de la computadora donde está instalado el Servidor del Firebird
  2. Crea una carpeta en la computadora Cliente
  3. Descomprime el archivo SQL_RENDIMIENTO.ZIP en esa carpeta
  4. Ejecuta el programa SQL_RENDIMIENTO.EXE

También podrías ejecutarlo desde un pen-drive pero eso no es recomendable pues allí tendrás que controlar una variable más: la velocidad del pen-drive, pues si es USB 3.0 será mucho más rápido que USB 2.0 y este será mucho más rápido que USB 1.0, y en una computadora Cliente podrías tener USB 2.0 y en otra USB 3.0 y en otra USB 1.0 y por lo tanto la comparación no sería justa.

Mientras el programa está verificando las velocidades va mostrando unos mensajes en el campo de edición “Comentarios” y al finalizar la verificación muestra un resúmen que puede servirte de guía para encontrar las fallas. Ese resúmen puede ser guardado en un archivo de texto (cuya ubicación y nombre debes colocar a continuación de: “Grabar en el archivo”).

Las demoras en el Cliente típicamente son mayores que las demoras en el Servidor porque el primero necesita que los datos vayan al Servidor y regresen y eso toma su tiempo. Es por eso que se muestran ambas demoras.

El programa califica con una nota (EXCELENTE, MUY BUENO, BUENO, ACEPTABLE, REGULAR, MALO, MUY MALO, PÉSIMO) a ambas computadoras, dependiendo del tiempo total con la cual se realizaron todas las operaciones. También da unos consejos (que en versiones posteriores podrían ampliarse) para mejorar esas velocidades. Debes tener en mente que esa calificación está basada en el procesamiento de 1.000.000 de filas y que si varías dicha cantidad de filas la calificación podría variar ya que cuantas más filas proceses el rendimiento mejorará (si multiplicas por 10 la cantidad de filas el tiempo que se demora en procesarlas el Firebird no se multiplica por 10, sino quizás por 2).

Otro punto muy importante a considerar es el siguiente: una Base de Datos totalmente vacía es mucho más rápida que una Base de Datos en la cual ya se realizaron operaciones aunque se hayan eliminado luego todas esas operaciones. ¿Qué significa esto? que siempre antes de ejecutar el programa SQL_RENDIMIENTO.EXE debes hacer un ciclo backup/restore en la Base de Datos VELOCIDAD.FDB y usar la Base de Datos restaurada para la verificación (o aún más rápido, copia la Base de Datos original, la que descargaste en el archivo .ZIP, en el Servidor) . Si no haces así, entonces cada nueva ejecución de SQL_RENDIMIENTO.EXE demorará más y eso no será justo

Entonces, antes de verificar una computadora:

  1. Copia el archivo VELOCIDAD.FDB que está en tu pen-drive en el disco duro del Servidor
  2. Crea una carpeta en el disco duro de la computadora Cliente y coloca allí a SQL_RENDIMIENTO
  3. Verifica esa computadora

Haz esto siempre, no te olvides, o los resultados mostrados no serán correctos.

Artículo relacionado:

El índice del blog Firebird21