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