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