Uno de los problemas que podemos tener con el uso de replicación de SQL Server es la aparición de problemas inesperados por falta de conocimiento o planificación adecuada. La replicación de SQL Server debe verse como una funcionalidad transversal que nos afectará a muchos más niveles de los que podríamos esperar inicialmente de una replicación de datos. La mayoría de DBAs que tengan que lidiar con replicación de SQL Server conocerán esto de primera mano. En este artículo vamos a centrarnos en cómo realizar un backup de una arquitectura con replicación transaccional.

Copias de seguridad

Cuando tenemos una arquitectura replicada tenemos que considerar nuestro publicador, distribuidor y subscriptor como un meta-servidor sobre el que realizamos copias de seguridad. En el caso de la replicación transaccional tenemos distintos agentes que deberemos tener en cuenta para poder tener una sincronización correcta cuando restauremos las bases de datos. El primero de ellos es el lector del log de transacciones que es el responsable de extraer del log de transacciones aquellas operaciones que deban replicarse y escribirlas en la base de datos de distribución. El segundo de ellos es el agente de distribución que es el responsable de leer estas operaciones de la base de datos de distribución y replicarlas al subscriptor.

Imaginemos por ejemplo que nuestras copias de seguridad están programadas para las 17:00 aproximadamente en todos nuestros servidores y que tras un desastre deseamos restaurar nuestros sistemas al estado que tenían a las 17 horas. Por motivos de diferencias de sincronización de relojes, duración de los backups, etc. la hora efectiva de finalización de los backups es la siguiente:

Escenario   Backup del publicador     Backup del distribuidor     Backup del subscriptor  

A

17:00

17:01

17:02

B

16:58

17:00

17:01

C

17:00

17:01

16:58

D

17:00

16:58

17:02

Vamos a trabajar sobre un supuesto en que nuestra última transacción replicada (un insert por ejemplo) se ejecutó el publicador a las 16:59 y que nuestra replicación está funcionando correctamente, sin latencias excesivas que puedan implicar retrasos de más de unos pocos segundos en la replicación de los cambios.

Escenario A

Una vez realizados los backups procederemos a detener los agentes de réplica antes de proceder con la restauración de las bases de datos. Para detenerlos utilizaremos el monitor de replicación o bien desactivaremos los jobs correspondientes:

Evitando problemas con la replicación de SQL Server Evitando problemas con la replicación de SQL Server

Una vez tenemos los agentes parados procederemos a restaurar las bases de datos respaldadas sin tener que seguir ningún orden particular para este proceso. Una vez restauradas las bases de datos pondremos en marcha los agentes e introducimos un nuevo dato en la tabla replicada. Vemos como el agente del log de transacciones es capaz de llevarlo a la base de distribución sin problemas y el agente de distribución de aplicar el cambio en el subscriptor:

Evitando problemas con la replicación de SQL Server Evitando problemas con la replicación de SQL Server

En resumen, realizando este backup de forma escalonada y teniendo asegurado que el último cambio ocurrió antes del backup del publicador y que todos los cambios se sincronizaron correctamente (sin latencias) la recuperación ha tenido éxito. Como podemos ver son muchas condiciones que en entornos reales probablemente no se pueden garantizar. Tampoco podemos determinar el instante exacto de finalización del backup ya que dependerá de muchos factores que están fuera de nuestro control.

Escenario B

Realizaremos los backups de forma que el backup del publicador termine antes del último cambio replicado. Dicho cambio sí pasará por el distribuidor y el subscriptor. Detendremos los agentes de réplica como en el caso anterior y restauraremos las bases de datos.

Una vez restauradas veremos cómo los datos se encuentran desincronizados, existiendo datos en el subscriptor que no existen en el publicador:

select * from origen.dbo.tabla

select * from destino.dbo.tabla

Evitando problemas con la replicación de SQL Server

Además el lector del log de transacciones se nos quejará de que realmente hemos hecho “un viaje en el tiempo no autorizado”:

Evitando problemas con la replicación de SQL Server

Básicamente en la base de datos de distribución tenemos almacenado el último LSN sincronizado y al intentarlo utilizar nos encontramos con que es un valor que no existe.

Escenario C

En este escenario será el backup del subscriptor el que termine antes que el último cambio replicado. Quedará registrado dicho cambio como replicado tanto en el publicador como en el distribuidor. Inicialmente antes de la sincronización las bases de datos estarán desincronizadas:

select * from origen.dbo.tabla

select * from destino.dbo.tabla

Evitando problemas con la replicación de SQL Server

Después de unos segundos, las tablas se sincronizarán:

Evitando problemas con la replicación de SQL Server

En este caso lo que hemos experimentado sería similar a si tenemos una desconexión temporal del subscriptor. Es decir, si desconectamos de la red el subscriptor y se generan cambios en el publicador llegarán al distribuidor y quedarán ahí almacenados tanto tiempo como periodo de retención tengamos configurado. Por lo tanto mientras no excedamos la “ventana” que nos da el periodo de retención podremos hacer el “replay” desde el punto en el que nos hubiésemos quedado.

Escenario D

En este escenario el backup del distribuidor terminará antes que el del publicador y el del subscriptor. Una vez tenemos los backups restaurados vemos que tenemos los datos sincronizados inicialmente:

select * from origen.dbo.tabla

select * from destino.dbo.tabla

Evitando problemas con la replicación de SQL Server

Sin embargo, si insertamos algún dato nuevo en el publicador veremos como el agente del log de transacciones no es capaz de llevar dicho cambio hasta el distribuidor:

Evitando problemas con la replicación de SQL Server

De nuevo el problema es un “viaje en el tiempo” en este caso del distribuidor que hace que cuando intente continuar por el LSN que tenía anotado éste se encuentre “por detrás” de la parte activa del log de transacciones del publicador.

Resumen de resultados

La siguiente tabla resume los resultados obtenidos en los distintos escenarios:

Escenario Backup del publicador Backup del distribuidor Backup del subscriptor   Resultado 

A

17:00

17:01

17:02

Sincronizado si no existe actividad
desde el último cambio respaldado en el publicador

B

16:58

17:00

17:01

Error en el agente lector del log de
transacciones (LSN incorrecto)

C

17:00

17:01

16:58

Es posible llegar a la sincronización si el backup
del subscriptor no es demasiado antiguo

D

17:00

16:58

17:02

Error en el agente lector del log de
transacciones (LSN incorrecto)

 

Restauración consistente – Escenario E

En un escenario de desastre nos encontraremos con el dilema de si realizamos una restauración consistente o no. La restauración consistente nos aporta beneficios de cara a mantener el funcionamiento de la réplica imperturbado a cambio de una mayor complejidad. La restauración inconsistente puede llevarnos a problemas de sincronización que nos fuercen a tener que reinicializar la réplica. Será por tanto este coste de reinicialización el que decida muy probablemente cual es nuestra mejor estrategia.

En el caso que deseemos tener un entorno consistente al restaurar deberemos buscar un mecanismo que nos asegure esto. Inicialmente se podría pensar en realizar una copia completa de seguridad seguida por un backup del log de transacciones en cada uno de los servidores pasados unos minutos desde el fin de cada uno de los backups. Esto nos permitiría encontrar un punto en el tiempo donde podamos restaurar todas las bases de datos utilizando la cláusula “STOPAT” del comando restore log. El inconveniente de esta propuesta es que la utilización de marcas de tiempo puede que no sean lo suficientemente precisas si en el momento en el que se realizaron los backups los relojes de los distintos servidores no se encontraban perfectamente sincronizados. Debemos encontrar una forma de asegurar que restauramos todas las bases de datos a un punto consistente independientemente del reloj.

Una forma de conseguir esto es utilizar transacciones distribuidas marcadas. Una transacción marcada es una transacción que se inicia con la cláusla WITH MARK ‘nombre marca’. Si hacemos dicha transacción distribuida entre los tres servidores conseguiremos tener una marca consistente a la que podremos recurrir cuando restauremos un backup del log de transacciones con la cláusula WITH STOPATMARK. Desgraciadamente la “marca” no es automáticamente traspasada cuando realizamos una transacción distribuida. Para solucionar esto crearemos una tabla de marcado en cada una de las bases de datos implicadas y un procedimiento almacenado para el marcado. La tabla podría ser bien un tabla sencilla que simplemente contenga un valor numérico o bien utilizar alguna tabla ya existente :

CREATE TABLE MARCAS (A INT PRIMARY KEY)

INSERT INTO MARCAS VALUES (1)

Crearemos también un procedimiento de marcado en cada una de ellas:

CREATE PROCEDURE marcar @nombre_marca nvarchar (255)

AS

BEGIN TRANSACTION @nombre_marca WITH MARK

UPDATE marcas SET a=a+1

COMMIT TRANSACTION;

Finalmente para proceder al marcado deberemos ejecutar dentro de una misma transacción distribuida estos tres procedimientos. Para ello crearemos en cualquiera de los servidores el siguiente procedimiento:

 

CREATE PROCEDURE marcar_todos @nombre_marca nvarchar (255)

AS

BEGIN TRANSACTION

EXEC publicador.origen.dbo.marcar @nombre_marca

EXEC distribuidor.distribution.dbo.marcar @nombre_marca

EXEC subscriptor.destino.dbo.marcar @nombre_marca

COMMIT TRANSACTION;

Antes de realizar el marcado deberemos realizar un backup completo de cada una de las bases de datos que nos sirva de origen para la cadena de log de cada uno de ellos. Una vez realizado el backup completo, procederemos al marcado ejecutando el procedimiento anterior:

EXEC marcar_todos ‘Marca replica’

Una vez la marca esté en el log de transacciones de cada una de las bases de datos, realizaremos un backup del log de cada una de las bases de datos. Este backup del log contendrá la marca que hemos realizado y será la que utilicemos para la restauración.

Cuando procedamos a la restauración de cada base de datos comenzaremos realizando el RESTORE del backup completo con la opción NORECOVERY para cada una de las bases de datos:

restore database origen from disk = ‘c:E_origen.bak’ with replace, norecovery

restore database distribution from disk = ‘c:E_distribution.bak’ with replace, norecovery

restore database destino from  disk = ‘c:E_destino.bak’ with replace, norecovery

Una vez restaurados los backups completos, restauraremos los backups del log utilizando la marca creada como punto de sincronización:

restore database origen from disk = ‘c:E_origen_log.bak’ with STOPATMARK=‘Marca replica’

restore database distribution from disk = ‘c:E_distribution_log.bak’ with STOPATMARK=‘Marca replica’

restore database destino from disk = ‘c:E_destino_log.bak’ with STOPATMARK=‘Marca replica’

Tras esta restauración todas las bases de datos se encontrarán consistentes en el mismo punto por lo que no tendremos problema alguno cuando iniciemos los agentes de la réplica. Existe la posibilidad de que inicialmente los datos no se encuentren sincronizados si en el momento en el tiempo en el que realizamos el marcado éstos no se encontraban sincronizados. Por ejemplo si cuando lanzamos la marca el agente del log de transacciones aún no ha leído una transacción recién confirmada. Sin embargo esto no será un problema ya que los agentes podrán continuar exactamente por el punto en el que se encontraban sin riesgos a inconsistencias de LSNs ni de datos.

 

0 Shares:
Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

You May Also Like