sql >> Base de Datos >  >> RDS >> PostgreSQL

Cómo actualizar PostgreSQL 11 a PostgreSQL 12 sin tiempo de inactividad

La forma de actualización de base de datos más intuitiva que se le ocurre es generar una réplica en una nueva versión y realizar una conmutación por error de la aplicación en ella; de hecho, funciona perfectamente en otros motores. Con PostgreSQL, esto solía ser imposible de forma nativa. Para realizar las actualizaciones, necesitaba pensar en otras formas de actualización, como usar pg_upgrade, volcar y restaurar, o usar algunas herramientas de terceros como Slony o Bucardo, todas ellas con sus propias advertencias. Esto se debe a la forma en que PostgreSQL solía implementar la replicación.

La replicación de transmisión de PostgreSQL (la replicación común de PostgreSQL) es una replicación física que replica los cambios a nivel de byte por byte, creando una copia idéntica de la base de datos en otro servidor. Este método tiene muchas limitaciones cuando se piensa en una actualización, ya que simplemente no puede crear una réplica en una versión de servidor diferente o incluso en una arquitectura diferente.

Desde PostgreSQL 10, ha implementado una replicación lógica incorporada que, a diferencia de la replicación física, puede replicar entre diferentes versiones principales de PostgreSQL. Esto, por supuesto, abre una nueva puerta para mejorar las estrategias.

En este blog, veremos cómo puede actualizar su PostgreSQL 11 a PostgreSQL 12 sin tiempo de inactividad mediante la replicación lógica.

Replicación lógica de PostgreSQL

La replicación lógica es un método para replicar objetos de datos y sus cambios, en función de su identidad de replicación (generalmente una clave principal). Se basa en un modo de publicación y suscripción, donde uno o más suscriptores se suscriben a una o más publicaciones en un nodo de publicación.

Una publicación es un conjunto de cambios generados a partir de una tabla o un grupo de tablas (también conocido como conjunto de replicación). El nodo donde se define una publicación se denomina editor. Una suscripción es el lado descendente de la replicación lógica. El nodo donde se define una suscripción se denomina suscriptor y define la conexión a otra base de datos y conjunto de publicaciones (una o más) a las que desea suscribirse. Los suscriptores extraen datos de las publicaciones a las que se suscriben.

La replicación lógica se construye con una arquitectura similar a la replicación de transmisión física. Se implementa mediante los procesos "walsender" y "apply". El proceso de walsender inicia la decodificación lógica de WAL y carga el complemento de decodificación lógica estándar. El complemento transforma los cambios leídos de WAL al protocolo de replicación lógica y filtra los datos de acuerdo con la especificación de publicación. Luego, los datos se transfieren de forma continua utilizando el protocolo de replicación de transmisión al trabajador de aplicación, que asigna los datos a las tablas locales y aplica los cambios individuales a medida que se reciben, en un orden transaccional correcto.

La replicación lógica comienza tomando una instantánea de los datos en la base de datos del editor y copiando eso al suscriptor. Los datos iniciales en las tablas suscritas existentes se capturan y se copian en una instancia paralela de un tipo especial de proceso de aplicación. Este proceso creará su propia ranura de replicación temporal y copiará los datos existentes. Una vez que se copian los datos existentes, el trabajador ingresa al modo de sincronización, lo que garantiza que la tabla se actualice a un estado sincronizado con el proceso de aplicación principal mediante la transmisión de cualquier cambio que haya ocurrido durante la copia de datos inicial mediante la replicación lógica estándar. Una vez que se realiza la sincronización, el control de la replicación de la tabla se devuelve al proceso de aplicación principal donde la replicación continúa con normalidad. Los cambios en el editor se envían al suscriptor a medida que ocurren en tiempo real.

Cómo actualizar PostgreSQL 11 a PostgreSQL 12 mediante la replicación lógica

Vamos a configurar la replicación lógica entre dos versiones principales diferentes de PostgreSQL (11 y 12) y, por supuesto, una vez que tenga esto funcionando, solo se trata de realizar una conmutación por error de la aplicación en el base de datos con la versión más nueva.

Vamos a realizar los siguientes pasos para poner en funcionamiento la replicación lógica:

  • Configurar el nodo de publicación
  • Configurar el nodo de suscriptor
  • Crear el usuario suscriptor
  • Crear una publicación
  • Crear la estructura de la tabla en el suscriptor
  • Crear la suscripción
  • Comprobar el estado de replicación

Así que comencemos.

En el lado del editor, vamos a configurar los siguientes parámetros en el archivo postgresql.conf:

  • escucha_direcciones: En qué dirección(es) IP escuchar. Usaremos '*' para todos.
  • wal_level: Determina cuánta información se escribe en el WAL. Vamos a establecerlo en "lógico".
  • max_replication_slots :especifica el número máximo de ranuras de replicación que puede admitir el servidor. Debe establecerse al menos en la cantidad de suscripciones que se espera que se conecten, más alguna reserva para la sincronización de tablas.
  • max_wal_senders: Especifica la cantidad máxima de conexiones simultáneas desde servidores en espera o clientes de respaldo de base de transmisión. Debe establecerse al menos igual que max_replication_slots más la cantidad de réplicas físicas que están conectadas al mismo tiempo.

Tenga en cuenta que algunos de estos parámetros requieren un reinicio del servicio de PostgreSQL para aplicar.

El archivo pg_hba.conf también debe ajustarse para permitir la replicación. Debe permitir que el usuario de replicación se conecte a la base de datos.

Basándonos en esto, configuremos el editor (en este caso, el servidor PostgreSQL 11) de la siguiente manera:

postgresql.conf:

listen_addresses = '*'

wal_level = logical

max_wal_senders = 8

max_replication_slots = 4

​pg_hba.conf:

# TYPE  DATABASE        USER            ADDRESS                 METHOD

host     all     rep1     10.10.10.131/32     md5

Debes cambiar el usuario (en este ejemplo rep1), que se usará para la replicación, y la dirección IP 10.10.10.131/32 por la IP que corresponde a tu nodo PostgreSQL 12.

En el lado del suscriptor, también requiere que se configure max_replication_slots. En este caso, debe establecerse al menos en la cantidad de suscripciones que se agregarán al suscriptor.

Los otros parámetros que también deben configurarse aquí son:

  • max_logical_replication_workers :especifica el número máximo de trabajadores de replicación lógica. Esto incluye trabajadores de aplicación y trabajadores de sincronización de tablas. Los trabajadores de replicación lógica se toman del grupo definido por max_worker_processes. Debe establecerse al menos en el número de suscripciones, nuevamente más alguna reserva para la sincronización de la tabla.
  • max_worker_processes :establece el número máximo de procesos en segundo plano que el sistema puede admitir. Es posible que deba ajustarse para adaptarse a los trabajadores de replicación, al menos max_logical_replication_workers + 1. Este parámetro requiere un reinicio de PostgreSQL.

Entonces, debe configurar el suscriptor (en este caso el servidor PostgreSQL 12) de la siguiente manera:

postgresql.conf:

listen_addresses = '*'

max_replication_slots = 4

max_logical_replication_workers = 4

max_worker_processes = 8

Como este PostgreSQL 12 será el nuevo nodo principal pronto, debe considerar agregar los parámetros wal_level y archive_mode en este paso, para evitar un nuevo reinicio del servicio más adelante.

wal_level = logical

archive_mode = on

Estos parámetros serán útiles si desea agregar una nueva réplica o para usar copias de seguridad de PITR.

En el editor, debe crear el usuario con el que se conectará el suscriptor:

world=# CREATE ROLE rep1 WITH LOGIN PASSWORD '*****' REPLICATION;

CREATE ROLE

El rol utilizado para la conexión de replicación debe tener el atributo REPLICACIÓN. El acceso para el rol debe configurarse en pg_hba.conf y debe tener el atributo LOGIN.

Para poder copiar los datos iniciales, el rol utilizado para la conexión de replicación debe tener el privilegio SELECT en una tabla publicada.

world=# GRANT SELECT ON ALL TABLES IN SCHEMA public to rep1;

GRANT

Crearemos la publicación pub1 en el nodo editor, para todas las tablas:

world=# CREATE PUBLICATION pub1 FOR ALL TABLES;

CREATE PUBLICATION

El usuario que creará una publicación debe tener el privilegio CREAR en la base de datos, pero para crear una publicación que publique todas las tablas automáticamente, el usuario debe ser un superusuario.

Para confirmar la publicación creada vamos a utilizar el catálogo pg_publication. Este catálogo contiene información sobre todas las publicaciones creadas en la base de datos.

world=# SELECT * FROM pg_publication;

-[ RECORD 1 ]+-----

pubname      | pub1

pubowner     | 10

puballtables | t

pubinsert    | t

pubupdate    | t

pubdelete    | t

pubtruncate  | t

Descripciones de columnas:

  • nombre de publicación :Nombre de la publicación.
  • propietario :Propietario de la publicación.
  • puballtables :si es verdadero, esta publicación incluye automáticamente todas las tablas de la base de datos, incluidas las que se crearán en el futuro.
  • publicación :si es verdadero, las operaciones INSERT se replican para las tablas de la publicación.
  • actualización de publicación :si es verdadero, las operaciones de ACTUALIZACIÓN se replican para las tablas de la publicación.
  • pubdelete :si es verdadero, las operaciones DELETE se replican para las tablas de la publicación.
  • pubtruncate :si es verdadero, las operaciones TRUNCATE se replican para las tablas de la publicación.

Como el esquema no está replicado, debe realizar una copia de seguridad en PostgreSQL 11 y restaurarla en su PostgreSQL 12. La copia de seguridad solo se realizará para el esquema, ya que la información se replicará en la versión inicial. transferir.

En PostgreSQL 11:

$ pg_dumpall -s > schema.sql

En PostgreSQL 12:

$ psql -d postgres -f schema.sql

Una vez que tenga su esquema en PostgreSQL 12, debe crear la suscripción, reemplazando los valores de host, dbname, usuario y contraseña con los que correspondan a su entorno.

PostgreSQL 12:

world=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=10.10.10.130 dbname=world user=rep1 password=*****' PUBLICATION pub1;

NOTICE:  created replication slot "sub1" on publisher

CREATE SUBSCRIPTION

Lo anterior iniciará el proceso de replicación, que sincroniza el contenido de la tabla inicial de las tablas en la publicación y luego comienza a replicar los cambios incrementales en esas tablas.

El usuario que crea una suscripción debe ser un superusuario. El proceso de solicitud de suscripción se ejecutará en la base de datos local con los privilegios de un superusuario.

Para verificar la suscripción creada puede utilizar el catálogo pg_stat_subscription. Esta vista contendrá una fila por suscripción para el trabajador principal (con PID nulo si el trabajador no se está ejecutando) y filas adicionales para los trabajadores que manejan la copia de datos inicial de las tablas suscritas.

world=# SELECT * FROM pg_stat_subscription;

-[ RECORD 1 ]---------+------------------------------

subid                 | 16422

subname               | sub1

pid                   | 476

relid                 |

received_lsn          | 0/1771668

last_msg_send_time    | 2020-09-29 17:40:34.711411+00

last_msg_receipt_time | 2020-09-29 17:40:34.711533+00

latest_end_lsn        | 0/1771668

latest_end_time       | 2020-09-29 17:40:34.711411+00

Descripciones de columnas:

  • subid :OID de la suscripción.
  • subnombre :Nombre de la suscripción.
  • pid :Id. de proceso del proceso de trabajo de suscripción.
  • relad :OID de la relación que está sincronizando el trabajador; nulo para el trabajador de aplicación principal.
  • received_lsn :última ubicación de registro de escritura anticipada recibida, el valor inicial de este campo es 0.
  • last_msg_send_time :hora de envío del último mensaje recibido del remitente WAL de origen.
  • last_msg_receipt_time :Hora de recepción del último mensaje recibido del remitente WAL de origen.
  • latest_end_lsn :última ubicación de registro de escritura anticipada informada al remitente WAL de origen.
  • última_hora_de_finalización :hora de la última ubicación de registro de escritura anticipada informada al remitente WAL de origen.

Para verificar el estado de la replicación en el nodo principal, puede usar pg_stat_replication:

world=# SELECT * FROM pg_stat_replication;

-[ RECORD 1 ]----+------------------------------

pid              | 527

usesysid         | 16428

usename          | rep1

application_name | sub1

client_addr      | 10.10.10.131

client_hostname  |

client_port      | 35570

backend_start    | 2020-09-29 17:40:04.404905+00

backend_xmin     |

state            | streaming

sent_lsn         | 0/1771668

write_lsn        | 0/1771668

flush_lsn        | 0/1771668

replay_lsn       | 0/1771668

write_lag        |

flush_lag        |

replay_lag       |

sync_priority    | 0

sync_state       | async

Descripciones de columnas:

  • pid :ID de proceso de un proceso emisor WAL.
  • usesysid :OID del usuario que inició sesión en este proceso de remitente WAL.
  • nombre de usuario :Nombre del usuario que inició sesión en este proceso de remitente WAL.
  • nombre_aplicación :Nombre de la aplicación que está conectada a este remitente WAL.
  • dirección_cliente :dirección IP del cliente conectado a este remitente WAL. Si este campo es nulo, indica que el cliente está conectado a través de un socket Unix en la máquina del servidor.
  • nombre_host_cliente :nombre de host del cliente conectado, según lo informado por una búsqueda inversa de DNS de client_addr. Este campo solo será no nulo para conexiones IP, y solo cuando log_hostname esté habilitado.
  • puerto_cliente :Número de puerto TCP que el cliente está usando para comunicarse con este remitente WAL, o -1 si se usa un socket Unix.
  • backend_start :Hora en que se inició este proceso.
  • backend_xmin :El horizonte xmin de este standby informado por hot_standby_feedback.
  • estado :Estado actual del remitente WAL. Los valores posibles son:inicio, puesta al día, transmisión, copia de seguridad y detención.
  • sent_lsn :última ubicación de registro de escritura anticipada enviada en esta conexión.
  • write_lsn :última ubicación de registro de escritura anticipada escrita en el disco por este servidor en espera.
  • flush_lsn :última ubicación de registro de escritura anticipada vaciada en el disco por este servidor en espera.
  • replay_lsn :Última ubicación de registro de escritura anticipada reproducida en la base de datos en este servidor en espera.
  • write_lag :tiempo transcurrido entre el vaciado local de WAL reciente y la recepción de la notificación de que este servidor en espera lo ha escrito (pero aún no lo ha vaciado ni aplicado).
  • flush_lag :tiempo transcurrido entre el vaciado local de WAL reciente y la recepción de la notificación de que este servidor en espera lo ha escrito y vaciado (pero aún no lo ha aplicado).
  • replay_lag :tiempo transcurrido entre el vaciado local de WAL reciente y la recepción de la notificación de que este servidor en espera lo ha escrito, vaciado y aplicado.
  • prioridad_sincronización :Prioridad de este servidor en espera para ser elegido como el servidor en espera síncrono en una replicación síncrona basada en prioridad.
  • estado_sincrónico :estado síncrono de este servidor en espera. Los valores posibles son asíncrono, potencial, sincronizado, quórum.

Para verificar cuándo finaliza la transferencia inicial, puede consultar el registro de PostgreSQL en el suscriptor:

2020-09-29 17:40:04.403 UTC [476] LOG:  logical replication apply worker for subscription "sub1" has started

2020-09-29 17:40:04.411 UTC [477] LOG:  logical replication table synchronization worker for subscription "sub1", table "city" has started

2020-09-29 17:40:04.422 UTC [478] LOG:  logical replication table synchronization worker for subscription "sub1", table "country" has started

2020-09-29 17:40:04.516 UTC [477] LOG:  logical replication table synchronization worker for subscription "sub1", table "city" has finished

2020-09-29 17:40:04.522 UTC [479] LOG:  logical replication table synchronization worker for subscription "sub1", table "countrylanguage" has started

2020-09-29 17:40:04.570 UTC [478] LOG:  logical replication table synchronization worker for subscription "sub1", table "country" has finished

2020-09-29 17:40:04.676 UTC [479] LOG:  logical replication table synchronization worker for subscription "sub1", table "countrylanguage" has finished

O revisando la variable srsubstate en el catálogo pg_subscription_rel. Este catálogo contiene el estado de cada relación replicada en cada suscripción.

world=# SELECT * FROM pg_subscription_rel;

 srsubid | srrelid | srsubstate | srsublsn

---------+---------+------------+-----------

   16422 |   16386 | r          | 0/1771630

   16422 |   16392 | r          | 0/1771630

   16422 |   16399 | r          | 0/1771668

(3 rows)

Descripciones de columnas:

  • srsubid :Referencia a la suscripción.
  • srrelid :Referencia a la relación.
  • srsubestado :Código de estado:i =inicializar, d =los datos se están copiando, s =sincronizados, r =listo (replicación normal).
  • srsublsn :finaliza el LSN para los estados s y r.

Puede insertar algunos registros de prueba en su PostgreSQL 11 y validar que los tiene en su PostgreSQL 12:

PostgreSQL 11:

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5001,'city1','USA','District1',10000);

INSERT 0 1

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5002,'city2','ITA','District2',20000);

INSERT 0 1

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5003,'city3','CHN','District3',30000);

INSERT 0 1

PostgreSQL 12:

world=# SELECT * FROM city WHERE id>5000;

  id  | name  | countrycode | district  | population

------+-------+-------------+-----------+------------

 5001 | city1 | USA         | District1 |      10000

 5002 | city2 | ITA         | District2 |      20000

 5003 | city3 | CHN         | District3 |      30000

(3 rows)

En este punto, tiene todo listo para apuntar su aplicación a su PostgreSQL 12.

Para esto, en primer lugar, debe confirmar que no tiene retraso de replicación.

En el nodo principal:

world=# SELECT  application_name,  pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) lag FROM pg_stat_replication;

-[ RECORD 1 ]----+-----

application_name | sub1

lag              | 0

Y ahora, solo necesita cambiar su punto final de su aplicación o balanceador de carga (si tiene uno) al nuevo servidor PostgreSQL 12.

Si tiene un balanceador de carga como HAProxy, puede configurarlo usando PostgreSQL 11 como activo y PostgreSQL 12 como respaldo, de esta manera:

Entonces, si cierra el antiguo nodo principal en PostgreSQL 11, el servidor de respaldo, en este caso en PostgreSQL 12, comienza a recibir el tráfico de forma transparente para el usuario/aplicación.

Al final de la migración, puede eliminar la suscripción en su nuevo nodo principal en PostgreSQL 12:

world=# DROP SUBSCRIPTION sub1;

NOTICE:  dropped replication slot "sub1" on publisher

DROP SUBSCRIPTION

Y verifica que se elimine correctamente:

world=# SELECT * FROM pg_subscription_rel;

(0 rows)

world=# SELECT * FROM pg_stat_subscription;

(0 rows)

Limitaciones

Antes de utilizar la replicación lógica, tenga en cuenta las siguientes limitaciones:

  • El esquema de la base de datos y los comandos DDL no se replican. El esquema inicial se puede copiar usando pg_dump --schema-only.
  • Los datos de la secuencia no se replican. Los datos en columnas seriales o de identidad respaldadas por secuencias se replicarán como parte de la tabla, pero la secuencia en sí seguirá mostrando el valor de inicio en el suscriptor.
  • Se admite la replicación de los comandos TRUNCATE, pero se debe tener cuidado al truncar grupos de tablas conectadas por claves foráneas. Al replicar una acción de truncado, el suscriptor truncará el mismo grupo de tablas que se truncó en el publicador, ya sea especificado explícitamente o recopilado implícitamente a través de CASCADE, menos las tablas que no forman parte de la suscripción. Esto funcionará correctamente si todas las tablas afectadas forman parte de la misma suscripción. Pero si algunas tablas que se van a truncar en el suscriptor tienen enlaces de clave externa a tablas que no forman parte de la misma (o ninguna) suscripción, la aplicación de la acción de truncado en el suscriptor fallará.
  • Los objetos grandes no se replican. No hay solución para eso, aparte de almacenar datos en tablas normales.
  • La replicación solo es posible de tablas base a tablas base. Es decir, las tablas en la publicación y en el lado de la suscripción deben ser tablas normales, no vistas, vistas materializadas, tablas raíz de partición o tablas foráneas. En el caso de las particiones, puede replicar una jerarquía de partición uno a uno, pero actualmente no puede replicar a una configuración con particiones diferentes.

Conclusión

Mantener actualizado su servidor PostgreSQL realizando actualizaciones periódicas ha sido una tarea necesaria pero difícil hasta la versión 10 de PostgreSQL. Afortunadamente, ahora es una historia diferente gracias a la replicación lógica.

En este blog, hicimos una breve introducción a la replicación lógica, una característica de PostgreSQL introducida de forma nativa en la versión 10, y le mostramos cómo puede ayudarlo a lograr este desafío de actualización de PostgreSQL 11 a PostgreSQL 12 con una estrategia de tiempo de inactividad cero.