martes, 31 de diciembre de 2013

Archivado de logs directo a cinta

El archivado de log de transacciones en DB2 permite varios métodos: no archivar los ficheros y dejarlos en el directorio de logs, archivar a disco, o archivar directamente a la plataforma de backup mediante un driver del cliente de backup (VENDOR).

La primera opción no es muy conveniente, ya que no permite distinguir a simple vista cuales son los logs activos y los logs ya completados, y el proceso de backup es más complicado. En el caso del archivado a disco, suele usarse para posteriormente, cada cierto tiempo, se ejecute un proceso planificado que guarde los logs en la plataforma de backup y borre los ficheros.

En cambio cuando se archiva directamente sobre la plataforma de backup, cada log que se completa (que todas las transacciones que contiene han sido confirmadas o canceladas) se envía inmediatamente al backup. Si no es posible archivarlo por algún motivo (servidor de backup parado o congestionado, problemas de red, etc), el fichero se mantendrá en el directorio de logs y se seguirá reintentando hasta que se consiga archivar.

Ventajas e inconvenientes de cada método

El archivado a disco tiene tres ventajas, es más fácil de configurar, todos los datos se guardan en una sola operación y se controla su funcionamiento a través de la planificación.

Por contra tiene otros inconvenientes, como que no tiene en cuanta una posible carga de transacciones mayor de la habitual (la planificación sigue siendo la misma independientemente de que se estén ejecutando procesos que generen más log del habitual), es muy incómodo para hacer recuperaciones grandes (es posible que sea necesario hacer varias restauraciones de logs si estos no caben en el directorio de archivado) alargando el tiempo de recuperación, y se corre el riesgo de perder los logs en caso de desastre si no han sido recogidos por el backup, ya que se mantienen en disco.

El archivado a cinta tiene como principal ventaja la simplicidad una vez que todo está correctamente configurado. Toda la gestión tanto de backup como de restauración es transparente. Esto es una enorme ventaja si se está usando replicación. En caso de problemas será capaz de recuperarse por sí mismo. Sin embargo para que el archivado funcione correctamente hay que tener en cuanta unos puntos que vamos a tratar a continuación.

Consideraciones a tener en cuenta para usar el archivado a cinta

Activación y desactivación de la base de datos
Cuando se activa/desactiva una base de datos, el gestor trunca el log activo e intenta archivar el fichero antes de realizar la operación. Eso puede significar que la activación/desactivación se demore hasta que el log sea archivado. En el caso de que la plataforma de backup esté congestionada, puede suponer un tiempo importante.

Esto es especialmente peligroso si una base de datos no se activa explícitamente, y se activa al recibir conexiones. Si el tiempo de archivado es mayor que el tiempo de timeout de conexión de la aplicación la base de datos nunca llegará a activarse completamente. Para evitar esto se debe tener la base de datos siempre activada explícitamente. Se debe añadir al script de arranque el comando:

db2 ACTIVATE DATABASE <nombre base de datos>

De esta forma esta situación no se dará nunca. Para evitar la demora en una parada/arranque manual, hay dos métodos para lograrlo, una cancelando el job de backup desde la consola de la plataforma de backup, y otro matando el proceso db2vend asociado a la base de datos. Este proceso el el encargado de hacer la llamada al driver del cliente de backup. Si matamos este proceso el archivado se cancelará, e inmediatamente la base de datos se activara o desactivará. Matar este proceso no tiene ninguna otra consecuencia negativa, ya que el gestor volverá a levantar el proceso, pero la operación de activación/desactivación ya estará terminada, y el archivado que se canceló se volverá a intentar lo antes posible.

Cantidad de logs generado mayor que cantidad de log archivado
Cuando se realizan transacciones que generan mucho log (historificaciones, borrados o actualizaciones masivas) puede darse el caso de que la velocidad con la que se genera el log sea mayor que la velocidad con la que se archiva. Este problema se produce normalmente por un motivo: el archivado de logs a cinta requiere muchos recursos para comenzar el archivado de cada uno de los logs (ubicar la cinta, cargarla, conseguir un drive…) comparado con el tiempo de transferencia de datos. Si generamos un log cada minuto y tarda en archivarse dos, y esta situación se mantiene durante el tiempo suficiente, inevitablemente terminaremos llenando el disco, impidiendo que la base de datos procese más transacciones.

Para evitar esta situación se debe de reducir el número de archivados que se realizan, y eso se consigue aumentando el tamaño del log. Si en la situación anteriormente expuesta el log tiene un tamaño de 10MB, y lo aumentamos a 100MB, pasaría de generarse un log cada minuto a uno cada diez, con lo que no tendríamos ningún problema de llenado de disco de logs.

Como norma general, conviene establecer un tamaño de log que limite el número de archivados diarios a un número concreto, con la carga habitual. Para eso tenemos que conocer la cantidad de log generado diariamente. Si se generan 10GB de log diarios y queremos que se realice un archivado por hora, el tamaño de log debería ser 10GB/24=425MB. Eso se puede hacer cambiando el parámetro LOGFILSIZ al número de páginas de 4K que correspondan, en este caso 10GB*1024*1024/24= aprox. 110000:

db2 UPDATE DB CFG USING LOGFILSIZ 110000

Requiere parada de base de datos para que aplique.

Como consecuencia de esto, la frecuencia de checkpoint (volcar los datos modificados de memoria a disco) también cambiará, pudiendo aumentar el tiempo de recuperación de la base de datos. Esta frecuencia la marca la variable SOFTMAX, que indica la cantidad de log que se ha de generar para que se produzca este chekpoint (también llamado LSN Gap trigger). Para mantener la frecuencia, debe modificar el valor en la misma proporción que el tamaño de log.

En nuestro caso anterior, si el valor SOFTMAX era de 520, eso significa que se estaría volcando a disco cada 52MB. Teniendo en cuenta los 10GB diarios de log, esto se produciría 197 veces al día, cada 7 minutos. Esto puede ser una frecuencia demasiado alta, y podría producir una sobrecarga innecesaria en disco. Podemos establecer una frecuencia de checkpoint de 30 minutos. Esto son aproximadamente 210MB. Como el tamaño de log era de 425MB deberemos establecer el SOFTMAX a 50 (el 50% del log).

db2 UPDATE DB CFG USING SOFTMAX 50

Hay que considerar que al modificar estos valores podría llegarse a los umbrales permitidos de estos parámetros (máximo de LOGFILSIZ o mínimo de SOFTMAX), por lo que puede darse el caso de que no podamos regular totalmente la frecuencia de archivado o el intervalo de checkpoint y deberemos adaptarnos a esos máximos y mínimos.

Por ejemplo, si el log generado en lugar de 10GB fueran 100GB, para archivar cada hora necesitaríamos ficheros de 4GB, que son excesivamente grandes. En estos casos de transaccionalidad tan alta, debemos reducir la frecuencia, en lugar de cada hora cada 10 minutos, con lo que bastaría con un tamaño de log de 682MB.

Monitorización del archivado
Cuando el archivado de logs se produce automáticamente, no suele haber nadie que controle posibles errores. En cambio si los procesos de archivado están planificados recibiremos el error en caso de fallo. Si por cualquier motivo el archivado no está funcionando, no sabremos nada hasta que se llene el log y la base de datos no pueda procesar más transacciones. Conviene monitorizar el file system de log, y tener espacio suficiente para "sobrevivir" en caso de contingencia. Si la cantidad de log diaria es de 10GB, tendríamos que dejar 30GB libres para poder aguantar tres días sin archivado. En cualquier caso esto no es exclusivo del archivado a cinta ; en el archivado a log es igualmente recomendable.

Resumen

El archivado de logs a cinta es muy recomendable, pero hay que cuidar estos aspectos:
  • Mantener la base de datos siempre activada
  • Tener en cuenta los procesos de archivado en el arranque y la parada
  • Dimensionar adecuadamente los parámetros LOGFILSIZ y SOFTMAX
  • Dimensionar y monitorizar correctamente el filesystem de log

Si se tienen en cuenta estos cuatro puntos, el archivado a log se convierte de largo en la opción más adecuada, más cómoda y más sencilla.

viernes, 13 de mayo de 2011

DB2 Express-C 9.7.4

Se ha liberado la versión 9.7.4 de DB2 Express-C

Las novedades aunque no son muchas con respecto a la versión 9.7.2, son, a mi parecer, interesantes, y lo aún más que haya desarrollado sobre DB2.

La primera son los triggers compuestos (compound triggrs). Ahora, en lugar de crearse un trigger para cada operación (INSERT, UPDATE y DELETE), se pueden controlar todas las operaciones sobre un sólo trigger. Aunque no marca una diferencia espectacular, lo hace realmente más comodo.


La segunda novedad es que se pueden usar campos de una tabla en la parte derecha de la operación LIKE, cuando hasta ahora sólo se podían usar constantes. Este cambio si que me parece realmente potente, permitiendo hacer joins con patrones, o, aunque de forma un tanto rudimientaria, usar una lista constantes, lo que nos ahorra escribir muchas condiciones en determinadas búsquedas.


Para buscar las entradas que empiecen por a, b, c o d Se puede escribir:

SELECT count(*) from A,table(values ('a%'),('b%'),('c%'),('d%'),('e%')) as t(a) where A.a like t.a;

o

with t(a) as(values ('a%'),('b%'),('c%'),('d%'),('e%'))
SELECT count(*) from A,t where A.a like t.a;


en lugar de:

SELECT count(*) from A where A.a like 'a%' or A.a like 'b%' or A.a like 'c%' or A.a like 'd%' or A.a like 'e%';

Por último aporta mejoras en el uso de Text Search.

martes, 22 de febrero de 2011

Block based bufferpools

En la versión 8 de DB2 UDB apareció una nueva característica, los bufferpools basados en bloques. Se supone que estos bufferpools reservan un espacio para que las lecturas asíncronas se hagan en bloque, según el parámetro BLOCKSIZE del bufferpool. Se tratarán de leer tantas páginas simultáneas como indique este valor. Al realizar una lectura asíncrona se leerá un extent del tablespace, y si este cabe en un bloque, lo guardará.

¿Y qué ventaja nos aporta esto? Pues realmente, la principal ventaja no es que se lea en bloque, porque eso ya lo hacen los bufferpools normales con Vectored IO, sino que se delimita un espacio en el bufferpool para lecturas asíncronas, de forma que no se van a desplazar páginas usadas frecuentemente cuando se haga una lectura muy grande. De esta forma no es necesario separar bufferpool para lectura síncrona y asíncrona, y si alguna tabla tiene accesos de ambos tipos, se tratarán ambos de forma independiente.

En resumen:
-No se desplazan páginas muy usadas del bufferpool
-No es necesario separar bufferpools para tablas de acceso síncrono y asíncrono
-Una tabla puede tener accesos síncronos y asíncronos sin ser perjudicada


Configuración de un block based bufferpool:

Al crear un bufferpool basado en bloques se definen dos parámetros, el BLOCKSIZE y el NUMBLOCKPAGES. El primero indica el número de página que contendrá cada bloque, y serán el número de páginas que se leerán simultáneamente. Es importante que este sea igual o mayor que el tamaño de extent de los tablespaces que usen este bufferpool, de lo contrario no se usará el área de bloques. El segundo es el número de páginas del área de bloques. Este debería ser un múltiplo del tamaño de bloque. Se recomienda que el tamaña del area sea entre un 2% y un 3% del tamaño total del bufferpool.

El comando para crear el bufferpool sería:

CREATE BUFFERPOOL <nombre_bp> SIZE <numpaginas> NUMBLOCKPAGES <paginas_area_bloques> BLOCKSIZE <tamaño_bloque> PAGESIZE <tamaño_pagina>

Si se quiere modificar uno ya existente:

ALTER BUFFERPOOL <nombre_bp> NUMBLOCKPAGES <paginas_area_bloques> BLOCKSIZE <tamaño_bloque>

Hay que tener en cuenta que los cambios en el área de bloques requieren reinicio de la base de datos (desactivarla y desconectar todos los usuarios).

Monitorización:

Para comprobar el uso que se está haciendo del área de bloques, se usa el snapshot de bufferpool:

get snapshot for bufferpools on <database>

Los valores importantes serán:

Asynchronous pool data page reads
Asynchronous pool index page reads
Asynchronous data read requests
Asynchronous index read requests
Block IOs
Pages from block IOs


A partir de estos valores podemos calcular las siguientes métricas:

Páginas leídas por bloque:
Pages from block IOs / Block IOs

Debe acercarse lo más posible al tamaño de bloque.

% de lecturas asíncronas leídas por bloques:
Block IOs / (Asynchronous data read requests + Asynchronous index read requests)

Debe tener un valor igual o muy cercano a 100. De lo contrario significaría que hay lecturas asíncronas que no se están resolviendo por bloques.

% de páginas asíncronas leídas por bloques:
Pages from block IOs / (Asynchronous pool data page reads + Asynchronous pool index page reads)

El igual que en la métrica anterior, el valor debe ser lo más cercano a 100 posible.

martes, 23 de noviembre de 2010

Tablas de staging (I) - Introducción

Una tabla de staging consiste en una tabla intermedia en una materialized query table(MQT), donde se almacenan los cambios que se han realizado en la tabla origen. Con esta tabla intermedia se consigue poder hacer refrescos de MQT incrementales, de forma que sólo se refrescan los datos nuevos o que hayan sido modificados.

La ventaja que aporta sobre las MQT gestionadas por el sistema es que permite realizar operaciones en la MQT sin producir bloqueos sobre la tabla origen, y sobre las tablas gestionadas por usuario que se puede hacer el refresco de únicamente los datos modificados.

Esto es ideal para logs e historificaciones, ya que permite hacer consultas sobre los datos sin afectar al proceso de escritura.

Este artículo se dividirá en tres partes:

Esta primera parte donde explico el funcionamiento básico de las tablas de staging, su creación y su operativa ; una segunda parte donde se analiza el impacto que tiene sobre la escritura con respecto a una tabla convencional ; y una parte final donde se analiza el impacto del refresco, tanto en la tabla origen como en la destino.


CREACIÓN DE TABLAS DE STAGING

Supongamos una tabla T1, que se quiere historificar en la tabla T1_HIS.

CREATE TABLE T1(
FECHA TIMESTAMP NOT NULL PRIMARY KEY,
DATO1 INTEGER,
DATO2 INTEGER
);


La tabla de histórico se creará de la siguiente forma:

CREATE TABLE T1_HIS AS (SELECT * FROM T1) DATA INITIALLY DEFERRED REFRESH DEFERRED;

De esta forma se historificarán todos los registros de la tabla T1.

Ahora se crearía la tabla de staging:

CREATE TABLE T1_STG FOR T1_HIS PROPAGATE IMMEDIATE;

SET INTEGRITY FOR T1_STG STAGING IMMEDIATE UNCKECHED;


Y se refrescarían los datos:

REFRESH TABLE T1_HIS NOT INCREMENTAL;

Desde este momento los datos quedarían sincronizados, y cualquier cambio en T1 quedaría registrado en T1_STG.

Si insertamos un registro en T1:

INSERT INTO T1 VALUES(CURRENT TIMESTAMP, 100,10000);

Podemos ver como queda guardado en T1_STG:

SELECT * FROM T1_STG;


FECHA DATO1 DATO2 GLOBALTRANSID GLOBALTRANSTIME OPERATIONTYPE
-------------------------- ----------- ----------- ----------------------------- --------------------------------- -------------
2010-11-23-09.47.19.742507 100 10000 x'00000000002015B9' x'20101123084719742595000000' 1

1 record(s) selected.



Ahora podemos hacer un refresco incremental:

REFRESH TABLE T1_HIS INCREMENTAL ALLOW WRITE ACCESS;

El comando REFRESH TABLE permite realizar el refresco permitiendo accesos en la MQT de escritura, de lectura o no permitiendo accesos.

Una vez ejecutado podemos ver que la tabla T1_STG está vacía, y que la tabla T1_HIS contiene el registro que se insertó en T1.



CONSIDERACIONES A TENER EN CUENTA

Haciendo pruebas he podido comprobar que para la actualización de la tabla MQT se realizan búsquedas por la clave primaria de la tabla origen, por lo que es importante añadirle a posteriori la misma clave primaria que a T1, ya que esta no se crea de forma automática. Las tablas MQT no permiten índices únicos, por lo que el índice debe ser regular.

CREATE INDEX T1_HIS_IDX ON T1_HIS(FECHA);

Sin este índice, el rendimiento se degrada considerablemente a medida que las tablas crecen.



OPERACIONES SOBRE TABLAS

Con el procedimiento arriba indicado se consigue mantener una copia de la tabla sobre la que se pueden hacer operaciones mientras en la tabla original se insertan registros, pero llegará un momento que se deberán vaciar las tablas, bien porque sólo se mantiene un determinado periodo de retención para estas tablas, o porque los datos ya se han historificado desde la MQT.

Para esto se pueden vaciar las tablas origen y destino con un LOAD en vacío normal:

LOAD FROM /dev/null OF DEL REPLACE INTO T1 NONRECOVERABLE;
LOAD FROM /dev/null OF DEL REPLACE INTO T1_HIS NONRECOVERABLE;


Para vaciar la tabla de stating se hará con:

SET INTEGRITY FOR T1_STG PRUNE;

Después de esto las tablas quedarán en CHECK PENDING STATE. Para volver a su estado normal:

SET INTEGRITY ON T1 IMMEDIATE CHECKED;
SET INTEGRITY ON T1_HIS MATERIALIZED QUERY IMMEDIATE UNCHECKED;
SET INTEGRITY ON T1_STG STAGING IMMEDIATE UNCHECKED;


De esta forma las tablas volverán a estar operativas y vacías. Para sincronizar cualquier posible cambio que haya entrado sincronizamos la tabla completa.

REFRESH TABLE T1_HIS NOT INCREMENTAL;


En el próximo artículo se verá como afecta el uso de tablas staging a la inserción en la tabla origen, ya que cada operación realizada efectuará una inserción en la tabla de staging.