45 formas de aumentar la velocidad de las bases de datos de Firebird

Deja un comentario

En un artículo (en inglés) publicado por la empresa IBSURGEON se detallan 45 formas de aumentar la velocidad de las operaciones que realizan los usuarios en una Base de Datos de Firebird.

El artículo en cuestión puedes encontrarlo aquí:

45 ways to speed-up Firebird database

como verás, casi todas esas formas ya fueron motivo de algún artículo de este blog. De todas maneras, siempre es importante leerlo y si tienes algún comentario será bueno conocerlo.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

 

Evitando confusiones con las fechas

Deja un comentario

¿En Firebird la fecha 06/05/2016 se refiere al 6 de mayo de 2016 o se refiere al 5 de junio de 2016? ¿Estás seguro? ¿Estás totalmente seguro? ¿Y la fecha 06-05-2016?

Ya habíamos visto en este artículo como se formatean las fechas en Firebird:

Guardando fechas

para evitar confusiones lo más recomendable es usar el nombre del mes en letras. O las tres primeras letras del nombre del mes.

Si escribimos:

Listado 1.

SELECT
   *
FROM
   VENTAS
WHERE
   VEN_FECHAX = '06/MAY/2016'

no hay confusión posible, es evidente que nos estamos refiriendo al 6 de mayo de 2016, jamás podríamos confundirnos con el 5 de junio de 2016.

Por eso, lo recomendable es usar los nombres de los meses con letras, no con números.

FECHA-1

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

Podemos usar los nombres de los meses en inglés o sus abreviaturas, inclusive mezclando mayúsculas con minúsculas. O sea que cualquiera de las siguientes alternativas funcionarán bien:

MiFecha = ’28/September/2016′

MiFecha = ‘September/28/2016′

MiFecha = ’28/SEPTEMBER/2016′

MiFecha = ’28/SEPTember/2016’

MiFecha = ‘SEP/28/2016′

MiFecha = ’28/SEP/2016’

El autor de este blog prefiere la última alternativa porque se escribe poco y se entiende perfectamente, claro que tú puedes elegir cualquiera de las otras pero lo importante es usar letras para identificar a los meses, no números.

Artículos relacionados:

Guardando fechas

El índice del blog Firebird21

El foro del blog Firebird21

 

Entendiendo la memoria caché

1 comentario

Este asunto de la memoria caché que usa el Servidor del Firebird parece que no está muy claro, así que trataré de explicarlo en este artículo.

¿Qué es la memoria caché?

Es una porción de la memoria RAM de esa computadora donde se encuentra el Servidor del Firebird.

¿Para qué sirve la memoria caché?

Para devolver muy rápidamente los resultados de los SELECT.

¿Qué se coloca en la memoria caché?

Las páginas que fueron leídas para devolver los resultados que necesita un SELECT.

¿Cómo se obtienen los resultados que necesita un SELECT?

Extrayendo de la Base de Datos toda la página (o páginas) donde se encuentra la fila deseada.

Por ejemplo, si el tamaño de la página es de 4.096 bytes y cada fila de la tabla CLIENTES ocupa 100 bytes entonces en cada página de la tabla CLIENTES se podrán colocar 4.096 / 100 = 40 filas.

En una página jamás se mezclan datos de varias tablas. Si una página se reservó para ser usada por la tabla CLIENTES entonces solamente filas de la tabla CLIENTES podrá contener esa página.

En nuestro ejemplo, cada página podrá contener 40 filas (o registros) de la tabla CLIENTES.

Si escribimos un SELECT como el siguiente:

Listado 1.

SELECT
   *
FROM
   CLIENTES
WHERE
   CLI_IDENTI = 12345

Nuestro SELECT nos devolverá una sola fila, la fila cuyo identificador es 12345, pero extrajo de la Base de Datos toda la página donde se encuentra esa fila, o sea que extrajo 40 filas aunque solamente 1 de esas filas nos muestre.

Pero ahora las 40 filas, la que nos mostró y las 39 restantes que no nos mostró, están en la memoria caché.

 ¿Por qué se usa la memoria caché?

Porque para cualquier programa de computadora es muchísimo más rápido leer algo que se encuentra en la memoria RAM que leer algo que se encuentra en el disco duro. Extraer de la memoria RAM es miles de veces más rápido que extraer del disco duro.

Si ahora ejecutamos este SELECT:

Listado 2.

SELECT
   *
FROM
   CLIENTES
WHERE
   CLI_IDENTI = 12346

Y la fila cuyo identificador es 12346 se encuentra en la misma página que la fila cuyo identificador es 12345 entonces la fila que tiene el identificador 12346 ya está en la memoria caché. Y como ya está en la memoria caché entonces el Servidor del Firebird extraerá de allí los datos de esa fila, no los obtendrá del disco duro. En consecuencia, devolverá esos datos muy rápidamente.

¿Cómo se diferencian Classic y SuperServer en el uso de la memoria caché?

En Classic cada conexión usa su propia memoria caché. En nuestro ejemplo, si Juan ejecutó el SELECT del Listado 1. y luego María ejecutó el SELECT del Listado 2. entonces María no se benefició del SELECT ejecutado por Juan, ya que la memoria caché de María es totalmente independiente de la memoria caché de Juan. Pero si Juan ejecutó el SELECT del Listado 1. y después Juan también ejecutó el SELECT del Listado 2. entonces sí Juan se benefició de ya tener en su memoria caché los datos que necesitaba.

En SuperServer la memoria caché es compartida entre todas las conexiones. En nuestro ejemplo, si Juan ejecutó el SELECT del Listado 1. y después María ejecutó el SELECT del Listado 2. entonces María obtuvo un gran beneficio: los datos que necesitaba ya se encontraban en la memoria caché y por eso los recibió muy rápidamente.

 ¿Cómo se establece el tamaño de la memoria caché?

El tamaño de la memoria caché se calcula con esta fórmula:

Tamaño_memoria_caché = Cantidad_de_páginas * Tamaño_de_cada_página

La Cantidad_de_páginas debe estar entre 0 y 131.072. El Tamaño_de_cada_página debe ser 4.096 bytes, 8.192 bytes, ó 16.384 bytes.

El Tamaño_memoria_caché por supuesto no debe ser mayor que la memoria RAM libre disponible en la computadora donde está instalado el Servidor del Firebird.

¿Cómo se elige el tamaño de cada página?

Al crear la Base de Datos se puede elegir el tamaño de sus páginas, que como ya vimos debe ser uno de los siguientes: 4.096, 8.192, 16.384

Si no se eligió uno de ellos entonces el Servidor del Firebird usará 4.096

Se puede cambiar el tamaño de cada página haciendo un ciclo backup/restore. Al hacer el restore tenemos la opción de elegir el tamaño que tendrá cada página de la Base de Datos restaurada.

¿Cómo se elige la cantidad de páginas?

Primero, debemos recordar que esa cantidad debe estar entre 50 y 131.072

Segundo, no debemos sobrepasar la memoria RAM disponible.

Ejemplo: la computadora donde se instaló el Servidor del Firebird tiene 32 Gb de memoria RAM. Dejaremos 16 Gb para ser usados por el Sistema Operativo y los demás programas. Por lo tanto, para nuestra memoria caché reservaremos los restantes 16 Gb.

Nuestra Base de Datos tiene páginas de 4.096 bytes

Memoria RAM disponible = 16 Gb = 16 * 1.024 * 1.024 * 1.024 = 17.179.869.184 bytes

Tamaño de cada página = 4.096 bytes

Cantidad de páginas = 4.194.304

No podemos usar 4.194.304 páginas, la cantidad máxima que podemos usar es 131.072, el Firebird no nos permite usar más.

Y como cada página ocupa 4.096 bytes entonces nuestra memoria caché puede ocupar como máximo 131.072 * 4.096 = 536.870.912 bytes = 0,5 Gb

Si la arquitectura es SuperServer y el tamaño de cada página es de 4.096 bytes entonces el caché no puede ser mayor que 0,5 Gb. O sea, que tener disponibles 16 Gb de RAM es un gran desperdicio.

Podemos aumentar el tamaño del caché usado por SuperServer si aumentamos el tamaño de cada página. El tamaño de página máximo que podemos usar es 16.384 bytes, entonces: 131.072 * 16.384 = 2.147.483.648 bytes = 2 Gb

En otras palabras, si la arquitectura es SuperServer la cantidad máxima de memoria RAM del Servidor que podemos usar es de 2 Gb. Ni un byte más.

Entonces, si tenemos la posibilidad de usar más de 2 Gb de memoria RAM (como en este ejemplo) lo que debemos hacer es cambiar la arquitectura a Classic.

En Classic la memoria caché es por conexión. O sea que en nuestros cálculos debemos considerar la cantidad de conexiones concurrentes que podemos tener como máximo. No la cantidad normal de conexiones ni la cantidad mínima de conexiones, sino la cantidad máxima de conexiones que podríamos tener.

Por ejemplo, tenemos 70 usuarios pero algunos se conectan más de una vez al mismo tiempo, hemos determinado que nunca hay más de 90 conexiones concurrentes pero por un motivo de seguridad supondremos que son 100.

El tamaño de  cada página de nuestra Base de Datos es de 4.096 bytes.

Entonces:

16 Gb / 100 / 4.096 = 16 * 1.024 * 1.024 * 1.024 / 100 / 4.096 = 41.943 páginas

41.943 páginas * 4.096 bytes = 171.798.691 bytes

O sea, que podremos tener hasta 100 conexiones concurrentes, cada una de ellas usando 171.798.691 bytes de la memoria RAM del Servidor como su propia memoria caché.

Y como 171.798.691 bytes de memoria para cada conexión es muchísimo, entonces todos los SELECT de todos los usuarios serán rapidísimos.

¿Dónde se especifica la cantidad de páginas que debe usar el Servidor del Firebird?

En el archivo FIREBIRD.CONF, que encontrarás en la misma carpeta donde instalaste al Firebird. En ese archivo existe una entrada llamada DefaultDbCachePages. Debes borrar el símbolo de numeral que se encuentra a la izquierda (el símbolo de numeral indica que todo lo que se encuentra a su derecha es un comentario) y escribir la cantidad de páginas que quieres tener.

CACHE-1

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

CUIDADO: La cantidad de páginas es por Base de Datos. Eso significa que si puedes tener 2 bases de datos abiertas debes dividir la cantidad calculada por 2. Si puedes tener 3 bases de datos abiertas debes dividir la cantidad calculada por 3, y así sucesivamente.

En el archivo FIREBIRD.CONF se especifica la cantidad de páginas que usarán todas las bases de datos, o sea que es el valor por defecto, el que se usará si no se elige otra cantidad. Si queremos especificar para una Base de Datos en particular entonces usamos al programa GFIX.EXE para eso. Su opción -buffers nos permite determinar la cantidad de páginas que usará esa Base de Datos.

CACHE-2

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

En la Captura 2. le estamos diciendo que use 1.200 páginas para la Base de Datos BLOG_DB.FDB

CACHE-3

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

Y en la Captura 3. podemos ver la cantidad de páginas (Page buffers) usadas y el tamaño (Page size) de cada página. Entonces, 1.200 * 4.096 = 4.915.200 bytes

Si la arquitectura es Classic entonces esos 4.915.200 bytes serán para cada conexión y si la arquitectura es SuperServer entonces esos 4.915.200 bytes serán para todas las conexiones.

¿Cómo se coloca a una tabla en la memoria caché?

En la memoria caché se colocan las páginas que necesitó un SELECT. No se colocan datos allí cuando se ejecuta un INSERT, un UPDATE, o un DELETE. Solamente cuando se ejecuta un SELECT. Para colocar a toda una tabla en la memoria caché podemos hacerlo mediante SELECT * FROM MiTabla o mediante SELECT COUNT(*) FROM MiTabla. Esta última opción es preferible porque es mucho más rápida que la primera. ¿El motivo? Pone a cada página de MiTabla en la memoria caché pero no devuelve el contenido de esas páginas al Cliente.

Resumiendo:

  • La memoria caché es una porción de la memoria RAM de la computadora donde se instaló al Servidor del Firebird
  • Se usa la memoria caché para devolver muy rápidamente los resultados de los SELECT
  • Si los datos que debe devolver un SELECT se encuentran en la memoria caché obtenerlos de allí será mucho más rápido que obtenerlos del disco duro
  • En la memoria caché no se colocan filas ni tablas, se colocan páginas
  • Cuando se ejecuta un SELECT el Servidor del Firebird no extrae de la Base de Datos las filas que necesita ese SELECT sino las páginas donde se encuentran esas filas
  • En Classic la memoria caché de cada conexión es independiente de la memoria caché de las otras conexiones. En SuperServer la memoria caché es compartida entre todas las conexiones.
  • Si el tamaño de la Base de Datos es menor que 2 Gb entonces es recomendable que la arquitectura sea SuperServer. Si el tamaño de la Base de Datos es mayor que 2 Gb entonces es recomendable que la arquitectura sea Classic.
  • En el archivo FIREBIRD.CONF se puede especificar la cantidad de páginas que usará cada Base de Datos. Para una Base de Datos en particular se puede cambiar la cantidad de páginas usando el programa GFIX.EXE con la opción -buffers
  • El tamaño de cada página de la Base de Datos se determina cuando es creada, puede ser cambiado más adelante mediante un ciclo backup/restore
  • Los comandos INSERT, UPDATE, y DELETE no colocan datos en la memoria caché. Solamente el comando SELECT lo hace
  • Para poner a toda una tabla en la memoria caché se puede escribir: SELECT COUNT(*) FROM MiTabla. Ese comando colocará a cada página de MiTabla en la memoria caché.

Artículos relacionados:

Entendiendo las páginas de la Base de Datos

Eligiendo el tamaño adecuado de las páginas de la Base de Datos

SuperServer: optimizando el tamaño del caché

Classic: optimizando el tamaño del caché

Optimizando SuperServer: poniendo toda la Base de Datos en la memoria caché

El índice del blog Firebird21

El foro del blog Firebird21

 

Optimizando SuperServer: poniendo toda la Base de Datos en la memoria caché

5 comentarios

Hay un truco que podemos utilizar para optimizar a SuperServer, funciona también en Classic pero solamente con bases de datos mucho más pequeñas.

Como recordarás, si las filas que devolverá un SELECT ya se encuentran en la memoria caché entonces la velocidad de respuesta será muy alta porque la memoria RAM es muchísimo más rápida que el disco duro. Entonces, la idea es tener a toda la Base de Datos (o al menos a las tablas más utilizadas cuando tener a toda la Base de Datos no es posible) en la memoria caché.

Para poner a toda la Base de Datos en la memoria caché podríamos ejecutar el siguiente stored procedure:

Listado 1.

CREATE PROCEDURE LLENAR_CACHE
AS
   DECLARE VARIABLE lcNombreTabla   VARCHAR(1024);
   DECLARE VARIABLE lcComando       VARCHAR(1024);
   DECLARE VARIABLE lnCantidadFilas INTEGER;
BEGIN
   
   FOR SELECT
      RDB$RELATION_NAME
   FROM
      RDB$RELATIONS
   INTO
      :lcNombreTabla
   DO BEGIN
      lcComando = 'SELECT COUNT(*) FROM ' || lcNombreTabla;
      EXECUTE STATEMENT lcComando INTO :lnCantidadFilas;
   END

END;

Luego de ejecutar el comando EXECUTE PROCEDURE LLENAR_CACHE y el comando COMMIT, toda la Base de Datos se encontrará en la memoria caché (si hay suficiente espacio en la memoria caché, por supuesto). ¿Por qué? Porque cada vez que se lee una página de una tabla esa página es puesta en la memoria caché. La función COUNT(*) lee cada página de una tabla y por consiguiente coloca a cada página de esa tabla en la memoria caché. El stored procedure LLENAR_CACHE realiza esa operación para todas las tablas de la Base de Datos. Como resultado final, todas las páginas de todas las tablas se encontrarán en la memoria caché, si es que hay allí suficiente espacio libre.

¿Y si no hay suficiente espacio libre?

Entonces podríamos poner en la memoria caché a las tablas más utilizadas en los informes.

Listado 2.

CREATE PROCEDURE LLENAR_CACHE
AS
BEGIN
   SELECT COUNT(*) FROM BANCOS;
   SELECT COUNT(*) FROM PRODUCTOS;
   SELECT COUNT(*) FROM CLIENTES;
   SELECT COUNT(*) FROM PROVEEDORES;
   SELECT COUNT(*) FROM STOCK;
   SELECT COUNT(*) FROM MONEDAS;
END; 

Eso hará que todas las operaciones de lectura se realicen súper rápido. Pero durante el trabajo normal diario muchas operaciones de INSERT, UPDATE, y DELETE se irán ejecutando y en la memoria caché ya no se encontrarán las últimas filas insertadas, actualizadas, y borradas.

¿Y entonces?

Entonces lo que debemos hacer es volver a ejecutar el stored procedure LLENAR_CACHE para que nuevamente en la memoria caché se encuentre toda la Base de Datos (como en el caso del Listado 1.) o las tablas más utilizadas (como en el caso del Listado 2.).

Ese proceso podríamos hacerlo manualmente (por ejemplo, cada hora) pero es mucho más inteligente pedirle al Sistema Operativo que realice esa tarea.

Para ello crearemos un archivo de script y un archivo batch.

Listado 3.

CONNECT MiBaseDatos.FDB USER SYSDBA PASSWORD masterkey;

-- Primera vez
EXECUTE PROCEDURE LLENAR_CACHE;
COMMIT;
SHELL PING 192.0.2.2 -n 1 -w 36000 > NUL;

-- Segunda vez
EXECUTE PROCEDURE LLENAR_CACHE;
COMMIT;
SHELL PING 192.0.2.2 -n 1 -w 5000 > NUL;

-- Finalizar
EXIT;

Escribimos el contenido del Listado 3. en el bloc de notas y lo grabamos con el nombre LLENAR_CACHE.SQL

¿Qué hace este archivo de script?

Primero, se conecta a nuestra Base de Datos. Segundo, ejecuta el stored procedure llamado LLENAR_CACHE. Tercero, para finalizar la transacción hace un COMMIT. Cuarto, ejecuta el comando PING del Sistema Operativo donde la dirección IP mostrada es una IP que nunca existe y se usa normalmente para pruebas, la opción -n indica la cantidad de repeticiones, la opción -w indica la cantidad de milisegundos (36.000 milisegundos = 1 hora), y al redirigir a NUL no se ve salida en la pantalla.

En el Listado 3. el stored procedure LLENAR_CACHE se ejecutó 2 veces, con una hora de diferencia entre esas ejecuciones. Si la Base de Datos se usará durante 10 horas seguidas cada día, entonces deberíamos escribir 10 veces el EXECUTE PROCEDURE, el COMMIT, y el SHELL. Para que el Listado 3. no sea muy largo solamente escribí 2 veces esos comandos, en tu caso podrías necesitar escribirlos 8 veces, 10 veces, 14 veces, etc.

Listado 4.

C:
CD "\ARCHIVOS DE PROGRAMA\FIREBIRD\FIREBIRD_2_5\BIN"
ISQL -INPUT C:\USERS\WALTER\DESKTOP\LLENAR_CACHE.SQL

¿Qué hace este archivo batch?

Primero, se ubica en la unidad C:. Segundo, se ubica en la carpeta donde se encuentra el programa ISQL.EXE. Tercero, ejecuta el programa ISQL.EXE con la opción -INPUT y el nombre completo del archivo de script.

En síntesis, ejecuta al programa ISQL.EXE pidiéndole que ejecute los comandos que se encuentran en el archivo de script.

¿Cómo ejecutamos ese archivo batch?

Tenemos dos formas:

  • Manualmente
  • Automáticamente

Manualmente sería haciendo clic en él cada día antes de que el primer usuario empiece a trabajar, por ejemplo a las 7:30 de cada día. El problema es que podríamos olvidarnos de hacer ese clic o llegamos tarde al trabajo o estamos de vacaciones, etc.

Automáticamente sería mediante el Programador de Tareas del Windows. Podríamos programar una tarea que se ejecute cada vez que se enciende el Servidor o cada día a las 7:30. También podríamos hacerlo en nuestra aplicación: si detectamos que nadie está usando la Base de Datos entonces ejecutamos el archivo batch.

Conclusión:

Si la arquitectura que usamos es SuperServer entonces podemos conseguir que los resultados de las consultas sean rapidísimos poniendo a toda la Base de Datos en la memoria caché. Si no tenemos suficiente memoria para poner a toda la Base de Datos en la memoria caché, entonces pondremos allí a las tablas más utilizadas.

Dependiendo de nuestro caso crearíamos un stored procedure como el mostrado en el Listado 1. o como el mostrado en el Listado 2.

Luego creamos un archivo de script como el mostrado en el Listado 3., el cual se encargará de llenar la memoria caché. Como los usuarios estarán insertando, actualizando, y borrando filas de las tablas el contenido de la memoria caché se irá quedando desactualizado. Por eso cada hora (o el tiempo que te parezca conveniente) se vuelve a llenar la memoria caché con el contenido actual de cada tabla.

Finalmente, creamos un archivo batch que se encargará de ejecutar al programa ISQL.EXE teniendo como entrada al archivo de script. Ese archivo batch debería ser ejecutado cada día antes de que el primer usuario se conecte a la Base de Datos. Esto podríamos hacerlo manualmente o mucho mejor, automáticamente.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Classic: optimizando el tamaño del caché

Deja un comentario

Podemos conseguir que las operaciones ejecutadas por los usuarios se realicen mucho más rápido si el tamaño del caché está optimizado.

Recuerda: este artículo se refiere a la arquitectura Classic, en otro artículo se explica como optimizar para SuperServer.

El tamaño del caché se calcula de la siguiente manera:

Tamaño_caché = Cantidad_de_páginas * Tamaño_de_cada_página

En Classic la memoria caché es exclusiva para cada conexión. Eso significa que si hay 1 conexión se usará x memoria, si hay 2 conexiones se usará 2x memoria, si hay 3 conexiones se usará 3x memoria, etc.

El que cada usuario use su propia memoria caché tiene su ventaja y su desventaja.

La ventaja es que cada conexión es independiente de las demás. Si una conexión está usando mucha memoria RAM las demás conexiones ni se enterarán, no se verán afectadas por eso.

La desventaja es que si en la conexión 1 se ejecutó un SELECT y en la conexión 2 se ejecuta luego ese mismo SELECT, la conexión 2 no se beneficiará.

¿Y cómo determinamos el tamaño de la memoria caché?

Mediante dos variables: la cantidad de páginas y el tamaño de cada página de la Base de Datos

Los tamaños de las páginas actualmente pueden ser: 4096 bytes, 8192 bytes, 16384 bytes

Como sugerencia (no es obligatorio hacerlo así, aunque se obtienen buenos resultados) podríamos determinar el tamaño de cada página según estas reglas:

  1. Tamaño de la Base de Datos menor que 128 Mb, usar 4096 bytes
  2. Tamaño de la Base de Datos entre 128 Mb y 1 Gb, usar 8192 bytes
  3. Tamaño de la Base de Datos mayor que 1 Gb, usar 16384 bytes

Ejemplo:

Tenemos una Base de Datos cuyo tamaño es de 15 Gb, cada página ocupa 16.384 bytes, hay 90 usuarios concurrentes y una memoria RAM de 32 Gb.

Primero, debemos tomar en cuenta que no toda la memoria RAM estará disponible para el Servidor del Firebird, porque el Sistema Operativo y otros programas usarán una porción de esa memoria RAM. Para este ejemplo supongamos que queremos reservar 16 Gb para uso del Servidor del Firebird.

Segundo, por defecto para Classic se usan 75 páginas por cada conexión. Eso implica que cada conexión usaría 75 * 16.384 bytes, o sea 1.228.800 bytes.

Calculando la cantidad de páginas:

Cantidad_páginas = Memoria_caché / Cantidad_usuarios / Tamaño_cada_página

Cantidad_páginas = 16 Gb / 90 / 16.384 = 16 * 1.024 * 1.024 * 1.024 / 90 / 16.384 = 11.650

Aunque ahora los usuarios son 90, por un motivo de seguridad es mejor usar un número más alto, la cantidad de usuarios podría aumentar en el futuro próximo y es mejor prever eso, entonces supondremos que los usuarios serán 100.

Cantidad_páginas = 16 Gb / 100 / 16.384 = 16 * 1.024 * 1.024 * 1.024 / 100 / 16.384 = 10.485

El Firebird requiere que la cantidad de páginas se encuentre entre 50 y 131.072, y como 10.485 está en el rango admitido no tendremos problemas.

Cada conexión usará entonces 10.485 * 16.384 bytes, o sea 171.786.240 bytes. Lo cual es más que suficiente para la gran mayoría de las aplicaciones y de los usuarios. Muy raramente alguien podría necesitar más (y en ese caso el Servidor del Firebird deberá realizar parte del procesamiento en el disco duro, lo cual demorará la entrega del resultado).

Colocando a toda la Base de Datos en la memoria caché

Si toda nuestra Base de Datos está en la memoria caché entonces todos los SELECT y todos los procesos serán rapidísimos porque los datos necesarios se extraerán de la memoria RAM, no del disco duro. Y la memoria RAM es muchísimo más rápida que el disco duro.

Una página de la Base de Datos se coloca en la memoria caché cuando es leída. Eso implica que si leemos todas las páginas de una Base de Datos y tenemos suficiente memoria caché entonces toda la Base de Datos estará en la memoria caché. ¿Verdad? Es muy sencillo.

Y para leer cada página de cada tabla lo más simple es usar la función agregada COUNT(). Cada vez que escribimos un SELECT COUNT(*) FROM MiTabla todas las páginas de MiTabla son colocadas en la memoria caché (si hay suficiente espacio disponible, desde luego).

Listado 1.

CREATE PROCEDURE LLENAR_CACHE
AS
   DECLARE VARIABLE lcNombreTabla   VARCHAR(1024);
   DECLARE VARIABLE lcComando       VARCHAR(1024);
   DECLARE VARIABLE lnCantidadFilas INTEGER;
BEGIN
   
   FOR SELECT
      RDB$RELATION_NAME
   FROM
      RDB$RELATIONS
   INTO
      :lcNombreTabla
   DO BEGIN
      lcComando = 'SELECT COUNT(*) FROM ' || lcNombreTabla;
      EXECUTE STATEMENT lcComando INTO :lnCantidadFilas;
   END

END;

Explicación:

En la tabla del sistema llamada RDB$RELATIONS se encuentran los nombres de todas las tablas de la Base de Datos. Mediante un FOR SELECT se obtiene cada uno de esos nombres y se lo usa para hallar la cantidad de filas de la tabla.

Despues de escribir EXECUTE PROCEDURE LLENAR_CACHE y su correspondiente COMMIT, todas las páginas de todas las tablas estarán dentro de la memoria caché (desde luego, si hay suficiente espacio en la memoria caché).

Colocando parte de la Base de Datos en la memoria caché

Si la arquitectura es Classic muy raramente podremos poner a toda la Base de Datos en la memoria caché de cada conexión. Como vimos en este ejemplo, el tamaño máximo de una Base de Datos tendría que ser de 171.786.240 bytes lo cual para los estándares actuales es muy poco.

Si nuestra Base de Datos es muy grande no podremos ponerla completa dentro de la memoria caché, en esos casos lo mejor es colocar allí a las tablas más usadas, escribiendo algo como:

Listado 2.

SELECT COUNT(*) FROM PRODUCTOS;
SELECT COUNT(*) FROM CLIENTES;
SELECT COUNT(*) FROM PROVEEDORES;
SELECT COUNT(*) FROM BANCOS;
COMMIT;

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

SuperServer: optimizando el tamaño del caché

8 comentarios

Podemos conseguir que las operaciones ejecutadas por los usuarios se realicen mucho más rápido si el tamaño del caché está optimizado.

Recuerda: este artículo se refiere a la arquitectura SuperServer, en otro artículo se explica como optimizar para Classic.

El tamaño del caché se calcula de la siguiente manera:

Tamaño_caché = Cantidad_de_páginas * Tamaño_de_cada_página

En SuperServer la memoria caché es compartida por todas las conexiones. Eso significa que tanto si hay 1 usuario conectado, ó 12 usuarios conectados, ó 247 usuarios conectados, la memoria RAM que se usará en el Servidor será siempre la misma, no variará.

El que todos los usuarios usen la misma memoria caché tiene su ventaja y su desventaja.

La ventaja es que si el usuario 1 ejecuta un SELECT y el usuario 2 poco después ejecuta ese mismo SELECT los resultados muy probablemente ya se encontrarán en la memoria caché y el usuario 2 los recibirá muy rápido. Eso porque traer los datos desde la memoria caché es muchísimo más rápido que traerlos desde el disco duro.

La desventaja es que si el usuario 1 ejecuta un stored procedure o un SELECT que requieren de mucha memoria les producirá un cuello de botella a todos los demás usuarios (al usuario 2, al usuario 3, al usuario 4, etc.) porque mientras el Servidor del Firebird esté ejecutando lo solicitado por el usuario 1 la memoria RAM disponible para todos los demás usuarios será muy poca.

Por lo tanto, cuanto mayor sea la cantidad de memoria RAM disponible para el caché, mucho mejor para todos.

¿Y cómo determinamos el tamaño de la memoria caché?

Mediante dos variables: la cantidad de páginas y el tamaño de cada página de la Base de Datos

Los tamaños de las páginas actualmente pueden ser: 4096 bytes, 8192 bytes, 16384 bytes

Como sugerencia (no es obligatorio hacerlo así, aunque se obtienen buenos resultados) podríamos determinar el tamaño de cada página según estas reglas:

  1. Tamaño de la Base de Datos menor que 128 Mb, usar 4096 bytes
  2. Tamaño de la Base de Datos entre 128 Mb y 1 Gb, usar 8192 bytes
  3. Tamaño de la Base de Datos mayor que 1 Gb, usar 16384 bytes

Ejemplo:

Tenemos una Base de Datos cuyo tamaño es de 1,8 Gb, cada página ocupa 16.384 bytes, hay 12 usuarios concurrentes y una memoria RAM de 32 Gb.

Primero, debemos tomar en cuenta que no toda la memoria RAM estará disponible para el Servidor del Firebird, porque el Sistema Operativo y otros programas usarán una porción de esa memoria RAM. Pero en este caso el tamaño de la Base de Datos es mucho menor que la memoria RAM, así que podemos usar mucha memoria para ella.

Segundo, por defecto para SuperServer se usan 2.048 páginas por cada Base de Datos. Eso implica que cada Base de Datos usaría 2.048 * 16.384 bytes, o sea 33.554.432 bytes. Pero recuerda que es memoria compartida por todos los usuarios, en nuestro ejemplo si el usuario 1 ejecuta un proceso tan intensivo que requiere 30.000.000 de bytes, para los 11 usuarios restantes solamente quedarían 3.554.432 bytes, o en promedio 3.554.432 / 11, o sea 323.130 bytes para cada usuario. Parece muy injusto, pero así funciona SuperServer.

Calculando la cantidad de páginas:

Cantidad_páginas = 16 Gb / 16.384 = 16 * 1.024 * 1.024 * 1.024 / 16.384 = 1.048.576

El resultado es correcto, sin embargo el Firebird no aceptará 1.048.576 páginas, ¿por qué no? porque la cantidad de páginas debe estar entre 50 y 131.072.

Si establecemos que la cantidad de páginas será 131.072 entonces cada Base de Datos usará 131.072 * 16.384 = 2.147.483.648 bytes o lo que es lo mismo, 2 Gb. Tamaño máximo que se puede reservar en la memoria caché para SuperServer. Y una consecuencia es que no deberías usar SuperServer en bases de datos cuyo tamaño sea superior a 2 Gb, ¿el motivo? no puedes poner a toda tu Base de Datos en la memoria caché.

Como nuestra Base de Datos ocupa solamente 1,8 Gb entonces podemos tenerla completa dentro de la memoria caché.

Colocando a toda la Base de Datos en la memoria caché

Si toda nuestra Base de Datos está en la memoria caché entonces todos los SELECT y todos los procesos serán rapidísimos porque los datos necesarios se extraerán de la memoria RAM, no del disco duro. Y la memoria RAM es muchísimo más rápida que el disco duro.

Una página de la Base de Datos se coloca en la memoria caché cuando es leída. Eso implica que si leemos todas las páginas de una Base de Datos y tenemos suficiente memoria caché entonces toda la Base de Datos estará en la memoria caché. ¿Verdad? Es muy sencillo.

Y para leer cada página de cada tabla lo más simple es usar la función agregada COUNT(). Cada vez que escribimos un SELECT COUNT(*) FROM MiTabla todas las páginas de MiTabla son colocadas en la memoria caché (si hay suficiente espacio disponible, desde luego).

Listado 1.

CREATE PROCEDURE LLENAR_CACHE
AS
   DECLARE VARIABLE lcNombreTabla   VARCHAR(1024);
   DECLARE VARIABLE lcComando       VARCHAR(1024);
   DECLARE VARIABLE lnCantidadFilas INTEGER;
BEGIN
   
   FOR SELECT
      RDB$RELATION_NAME
   FROM
      RDB$RELATIONS
   INTO
      :lcNombreTabla
   DO BEGIN
      lcComando = 'SELECT COUNT(*) FROM ' || lcNombreTabla;
      EXECUTE STATEMENT lcComando INTO :lnCantidadFilas;
   END

END;

Explicación:

En la tabla del sistema llamada RDB$RELATIONS se encuentran los nombres de todas las tablas de la Base de Datos. Mediante un FOR SELECT se obtiene cada uno de esos nombres y se lo usa para hallar la cantidad de filas de la tabla.

Despues de escribir EXECUTE PROCEDURE LLENAR_CACHE y su correspondiente COMMIT, todas las páginas de todas las tablas estarán dentro de la memoria caché (desde luego, si hay suficiente espacio en la memoria caché).

Colocando parte de la Base de Datos en la memoria caché

Si nuestra Base de Datos es muy grande no podremos ponerla completa dentro de la memoria caché, en esos casos lo mejor es colocar allí a las tablas más usadas, escribiendo algo como:

Listado 2.

SELECT COUNT(*) FROM PRODUCTOS;
SELECT COUNT(*) FROM CLIENTES;
SELECT COUNT(*) FROM PROVEEDORES;
SELECT COUNT(*) FROM BANCOS;
COMMIT;

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Conectando a una Base de Datos desde dos servidores

2 comentarios

Si tu intención es corromper, destruir, destrozar internamente, a una Base de Datos del Firebird, esta es una de las formas más seguras de conseguirlo.

Así que si tu intención es mantener a la Base de Datos en muy buen estado de conservación, jamás hagas algo así.

Veamos un poco más detalladamente lo que sucedería si intentas conectarte a una misma Base de Datos desde dos (o más) servidores de Firebird al mismo tiempo.

Como recordarás, Firebird tiene tres arquitecturas: Classic, SuperClassic, SuperServer.

Classic y SuperClassic son ejecutados a través de un programa que se llama FB_INET_SERVER.EXE y realizan los bloqueos correctos usando el proceso FB_LOCK_MGR.

Entonces, aunque cada conexión usando Classic y SuperClassic se hace mediante servidores distintos (cada instancia ejecuta a un FB_INET_SERVER.EXE), el proceso FB_LOCK_MGR se encarga de gestionar esas conexiones asegurándose de que la Base de Datos no se corrompa.

SuperServer por otro lado es ejecutado a través de un programa que se llama FBSERVER.EXE, el cual requiere acceso exclusivo a la Base de Datos así que resulta imposible que otro Servidor pueda acceder a una Base de Datos a la cual alguien ya se conectó usando SuperServer. Y también que si alguien ya se conectó a una Base de Datos (usando Classic, SuperClassic, o SuperServer)  alguien más pueda conectarse a ella usando otro Servidor SuperServer.

Entonces la situación es la siguiente:

  • Si alguien se conectó a una Base de Datos usando SuperServer, nadie más podrá conectarse a esa Base de Datos usando otro Servidor (sea Classic, SuperClassic, o SuperServer dicho Servidor).
  • Si alguien se conectó a una Base de Datos (usando Classic, SuperClassic, o SuperServer), conectarse a esa Base de Datos usando otro Servidor, que sea SuperServer, será imposible.
  • Si alguien se conectó a una Base de Datos usando Classic o SuperClassic conectarse a esa Base de Datos usando otro Servidor que sea Classic o SuperClassic, sí es posible … pero corromperá a la Base de Datos.

El último punto es el peligroso.

¿Y por qué se corromperá la Base de Datos?

Hay dos cosas que pueden salir mal, muy mal.

Primero. Los índices estarán corruptos. Si desde el Servidor 1 se están insertando filas a una tabla y desde el Servidor 2 también se están insertando filas a esa misma tabla, cada Servidor actualizará los índices de la tabla, al mismo tiempo, y eso ocasionará un desbarajuste mayúsculo. Esto es solucionable. Cuando ningún Servidor está insertando ni borrando ni modificando filas de esa tabla se reconstruyen los índices y asunto solucionado. Pero si se trata de una tabla que tiene muchos movimientos tantas reconstrucciones de índices afectarán gravemente al rendimiento y además mientras tanto las consultas mostrarán datos incorrectos y los procesos también realizarán actualizaciones incorrectas.

Segundo. Un Servidor sobre-escribe las páginas del otro Servidor. Esto es gravísimo porque causará corrupción en el 100% de los casos. Como recordarás, Firebird guarda todo, absolutamente todo lo que se hace en una Base de Datos en bloques de bytes llamados páginas. Si hay dos servidores accediendo a una misma Base de Datos, no se tendrá una buena administración de esas páginas porque cada Servidor llamará a un proceso FB_LOCK_MGR diferente. Y cada uno de esos procesos guarda el estado actual de las páginas de la Base de Datos en una porción de memoria RAM a la cual tiene acceso exclusivo.Por ejemplo: el Servidor 1 necesita una nueva página para datos de la tabla VENTAS, ve que la página número 123456 está libre y marca esa página para contener datos de la tabla VENTAS. Todo bien hasta ahí. Pero el Servidor 2 también necesita una nueva página, en este caso para guardar los índices de la tabla PRODUCTOS, ve que la página número 123456 está libre y marca a esa página para contener los índices de la tabla PRODUCTOS. ¿Consecuencia? Que las ventas que se guardaron en la página 123456 se perdieron totalmente. Y que los índices que correspondían a esas ventas están huérfanos. Los productos no tendrán problemas, pero las ventas se corrompieron. Por supuesto que más adelante la situación puede ser a la inversa y los datos corruptos son los correspondientes a la tabla de PRODUCTOS. El problema por lo tanto es que si dos o más servidores están conectados a una misma Base de Datos la correcta administración de las páginas de esa Base de Datos será imposible, y la corrupción no tardará en llegar.

Conclusión:

Conectarse a una misma Base de Datos usando dos servidores, siendo al menos uno de esos servidores SuperServer, es imposible.

Si los servidores son Classic o SuperClassic la conexión sí es posible, pero con toda seguridad causará corrupción a la Base de Datos.

¿Por qué se corromperá la Base de Datos?

Un problema se tendrá con los índices, y aunque esto es solucionable será muy molesto porque se tendrá que reconstruirlos constantemente y eso hará que todas las operaciones se vuelvan muy lentas, o que las consultas muestren datos inconsistentes.

Pero el problema realmente grave se tendrá con las páginas de datos que fueron escritas por uno de los servidores y sobre-escritas por el otro Servidor. Esto ocurrirá porque cada Servidor guarda en una porción de la memoria RAM  (que es de su uso exclusivo) el estado de las páginas de la Base de Datos. Por lo tanto, lo que hace un Servidor el otro Servidor no lo sabe, lo ignora completamente.

Y claro, un Servidor marca una página para que guarde algo, y el otro Servidor marca a esa misma página para que guarde otra cosa. ¿Consecuencia? Corrupción asegurada, 100% asegurada.

Artículos relacionados:

Entendiendo las páginas de la Base de Datos

Eligiendo el tamaño adecuado de las páginas de la Base de Datos

El índice del blog Firebird21

El foro del blog Firebird21

 

 

 

Older Entries

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 342 seguidores