En esta entrada de blog hablaremos sobre la replicación lógica en PostgreSQL:sus casos de uso, información general sobre el estado de esta tecnología y un caso de uso especial en particular sobre cómo configurar un nodo de suscriptor (réplica) del servidor principal para para funcionar como el servidor de base de datos para el entorno de prueba y los desafíos superados.
Introducción
La replicación lógica, presentada oficialmente en PostgreSQL 10, es la tecnología de replicación más reciente que ofrece la comunidad de PostgreSQL. La replicación lógica es una continuación del legado de la replicación física con la que comparte muchas ideas y código. La replicación lógica funciona como la replicación física usando WAL para registrar cambios lógicos independientemente de la versión o arquitectura específica. Para poder proporcionar replicación lógica a la oferta central, la comunidad de PostgreSQL ha recorrido un largo camino.
Tipos de replicación e Historia de la replicación de PostgreSQL
Los tipos de replicación en bases de datos se pueden clasificar de la siguiente manera:
- Replicación física (también conocida como binaria)
- Nivel del sistema operativo (replicación de vSphere)
- Nivel del sistema de archivos (DRBD)
- Nivel de base de datos (basado en WAL)
- Replicación lógica (nivel de base de datos)
- Basado en activadores (DBMirror, Slony)
- Middleware (pgpool)
- Basado en WAL (pglogical, replicación lógica)
La hoja de ruta que lleva a la replicación lógica basada en WAL de hoy fue:
- 2001:DBMirror (basado en activadores)
- 2004:Slony1 (basado en activadores), pgpool (middleware)
- 2005:PITR (basado en WAL) introducido en PostgreSQL 8.0
- 2006:modo de espera cálido en PostgreSQL 8.2
- 2010:replicación de transmisión física, espera activa en PostgreSQL 9.0
- 2011:replicación de transmisión síncrona en PostgreSQL 9.1
- 2012:replicación de transmisión en cascada en PostgreSQL 9.2
- 2013:Trabajadores en segundo plano en PostgreSQL 9.3
- 2014:API de decodificación lógica, ranuras de replicación. (Las bases para la replicación lógica) en PostgreSQL 9.4
- 2015:2ndQuadrant presenta pglogical, el ancestro o la replicación lógica
- 2017:¡Replicación lógica en el núcleo de PostgreSQL 10!
Como podemos ver, muchas tecnologías colaboraron para hacer realidad la replicación lógica:archivado WAL, esperas activas/activas, replicación WAL física, trabajadores en segundo plano, decodificación lógica. Suponiendo que el lector esté familiarizado con la mayoría de las nociones de replicación física, hablaremos sobre los componentes básicos de la replicación lógica.
Conceptos básicos de la replicación lógica de PostgreSQL
Alguna terminología:
- Publicación: Un conjunto de cambios de un conjunto de tablas definidas en una base de datos específica en un servidor primario de replicación física. Una publicación puede manejar todo o parte de:INSERTAR, ELIMINAR, ACTUALIZAR, TRUNCAR.
- Nodo de publicador: El servidor donde reside la publicación.
- Identidad de réplica: Una forma de identificar la fila en el lado del suscriptor para ACTUALIZACIONES y ELIMINACIONES.
- Suscripción: Una conexión a un nodo de publicación y una o más publicaciones en él. Una suscripción utiliza una ranura de replicación dedicada en el publicador para la replicación. Se pueden usar ranuras de replicación adicionales para el paso de sincronización inicial.
- Nodo de suscriptor: El servidor donde reside la suscripción.
La replicación lógica sigue un modelo de publicación/suscripción. Uno o más suscriptores pueden suscribirse a una o más publicaciones en un nodo de publicación. Los suscriptores pueden volver a publicar para permitir la replicación en cascada. La replicación lógica de una tabla consta de dos etapas:
- Tomar una instantánea de la tabla en el publicador y copiarla al suscriptor
- Aplicar todos los cambios (desde la instantánea) en la misma secuencia
La replicación lógica es transaccional y garantiza que el orden de los cambios que se aplican al suscriptor sigue siendo el mismo que en el publicador. La replicación lógica brinda mucha más libertad que la replicación física (binaria), por lo tanto, se puede usar de más formas:
- Replicación específica de una sola base de datos o tabla (no es necesario replicar todo el clúster)
- Establecer disparadores para el suscriptor para una tarea específica (como anonimizar, que es un tema bastante candente después de que entró en vigor el RGPD)
- Hacer que un nodo de suscriptor recopile datos de muchos nodos de publicación, lo que permite un procesamiento analítico central
- Replicación entre diferentes versiones/arquitecturas/plataformas (actualizaciones sin tiempo de inactividad)
- Utilizar el nodo suscriptor como servidor de base de datos para un entorno de prueba/desarrollo. La razón por la que queremos esto es porque la prueba con datos reales es el tipo de prueba más realista.
Advertencias y restricciones
Hay ciertas cosas que debemos tener en cuenta al usar la replicación lógica, algunas de ellas pueden influir en algunas decisiones de diseño, pero otras pueden conducir a incidentes críticos.
Restricciones
- Solo se admiten operaciones DML. Sin DDL. El esquema tiene que ser definido de antemano
- Las secuencias no se replican
- Los objetos grandes no se replican
- Solo se admiten tablas base simples (no se admiten vistas materializadas, tablas raíz de partición, tablas externas)
Advertencias
El problema básico que tarde o temprano tendremos que enfrentar al usar la replicación lógica son los conflictos en el suscriptor. El suscriptor es un servidor normal de lectura/escritura que puede actuar como principal en una configuración de replicación física o incluso como publicador en una configuración de replicación lógica en cascada. Siempre que se realicen escrituras en las tablas suscritas, puede haber conflictos . Surge un conflicto cuando los datos replicados violan una restricción en la tabla a la que se aplican. Por lo general, la operación que causa esto es INSERTAR, ELIMINAR o ACTUALIZAR, que no tienen ningún efecto debido a que faltan filas, no causarán un conflicto. Cuando surge un conflicto, la replicación se detiene. El trabajador lógico en segundo plano se reiniciará en el intervalo especificado (wal_retrieve_retry_interval); sin embargo, la replicación fallará nuevamente hasta que se resuelva la causa del conflicto. Esta es una condición crítica que debe tratarse de inmediato. Si no lo hace, la ranura de replicación se atascará. en su posición actual, el nodo de publicación comenzará a acumular WAL e, inevitablemente, el nodo de publicación se quedará sin espacio en disco . Un conflicto es la razón más común por la que la replicación puede detenerse, pero cualquier otra condición errónea tendrá el mismo efecto:p. agregamos una nueva columna NOT NULL en una tabla suscrita pero olvidamos definir un valor predeterminado, o agregamos una columna en una tabla publicada pero olvidamos definirla en la tabla suscrita, o cometimos un error en su tipo y los dos tipos no son compatible. Todos esos errores detendrán la replicación. Hay dos formas de resolver un conflicto:
- Resolver el problema real
- Omita la transacción fallida llamando a pg_replication_origin_advance
Solución b. como también se muestra aquí, puede ser peligroso y complicado, ya que es básicamente un proceso de prueba y error, y si uno elige el LSN actual en el editor, podría terminar fácilmente con un sistema de replicación roto, ya que podría haber operaciones entre el LSN problemático y el LSN actual que nos gustaría conservar. Entonces, la mejor manera es resolver el problema del lado del suscriptor. P.ej. si surge una infracción de CLAVE ÚNICA, es posible que actualicemos los datos del suscriptor o simplemente eliminemos la fila. En un entorno de producción, todo esto debe estar automatizado o al menos semiautomatizado.
Configuración de los nodos de editor y suscriptor
Para obtener una descripción general de la replicación lógica en la práctica, lea este blog.
Los parámetros relevantes para la replicación lógica son:
- Lado del editor
- wal_level>=“lógico”
- max_replication_slots>=#suscripciones + sincronización de tabla inicial
- max_wal_senders>=max_replication_slots + other_physical_standbys
- Lado del suscriptor
- max_replication_slots>=#suscripciones
- max_logical_replication_workers>=#subscriptions + sincronización de tabla inicial
- max_worker_processes>=max_logical_replication_workers + 1 + max_parallel_workers
Nos centraremos en las consideraciones especiales que surgen de nuestro propósito especial para el que necesitamos la replicación lógica:crear un clúster de base de datos de prueba para que lo use el departamento de pruebas . La publicación puede definirse para todas las tablas o tabla por tabla. Sugiero el enfoque mesa por mesa, ya que nos da la máxima flexibilidad. Los pasos generales se pueden resumir de la siguiente manera:
- Realice un initdb nuevo en el nodo del suscriptor
- Vuelque el esquema del clúster de publicación y cópielo en el nodo de suscriptor
- Cree el esquema en el suscriptor
- Decida qué tablas necesita y cuáles no.
Con respecto a la viñeta anterior, hay dos razones por las que es posible que no necesite replicar una tabla o configurarla para la replicación:
- Es una tabla ficticia sin importancia (y tal vez también debería eliminarla de producción)
- es una tabla local para el entorno de producción, lo que significa que tiene mucho sentido que la misma tabla en el entorno de prueba (suscriptor) tenga sus propios datos
Todas las tablas que participan en la replicación lógica deben tener una IDENTIDAD DE REPLICA. Esta es por defecto la CLAVE PRINCIPAL, y si no está disponible, se puede definir una clave ÚNICA. Siguiente paso para encontrar el estado de las tablas con respecto a la IDENTIDAD DE LA RÉPLICA.
- Encuentre las tablas sin un candidato obvio para IDENTIDAD DE RÉPLICA
select table_schema||'.'||table_name from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name NOT IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY','UNIQUE')) AND table_schema NOT IN ('information_schema','pg_catalog') ;
- Encuentre las tablas sin CLAVE PRINCIPAL pero con un ÍNDICE ÚNICO
select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'UNIQUE' EXCEPT select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'PRIMARY KEY';
- Revise las listas anteriores y decida qué hacer con cada tabla
- Cree la publicación con las tablas para las que existe un PK
select 'CREATE PUBLICATION data_for_testdb_pub FOR TABLE ONLY ' || string_agg(qry.tblname,', ONLY ') FROM (select table_schema||'.'||quote_ident(table_name) as tblname from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY')) AND table_schema NOT IN( 'information_schema','pg_catalog') ORDER BY 1) as qry; \gexec
- Luego, cree la suscripción en el nodo de suscriptor
Lo anterior también copiará los datos.create subscription data_for_testdb_pub CONNECTION 'dbname=yourdb host=yourdbhost user=repmgr' PUBLICATION data_for_testdb_pub ;
- Agregue las tablas que desee que tengan un índice ÚNICO
Ejecute tanto en los nodos de editor como de suscriptor, por ejemplo:
En el editor:ALTER TABLE someschema.yourtable REPLICA IDENTITY USING INDEX yourindex_ukey;
En el suscriptor:ALTER PUBLICATION data_for_testdb_pub ADD TABLE ONLY someschema.yourtable;
ALTER SUBSCRIPTION data_for_testdb_pub REFRESH PUBLICATION WITH ( COPY_DATA );
- En este punto (sincronización), siempre debe tener un ojo en el registro de PostgreSQL en el nodo del suscriptor. No desea ningún error ni nada (tiempo de espera) que prohíba la continuación de la replicación lógica. RESUELVE CUALQUIER ERROR INMEDIATAMENTE , o el editor seguirá acumulando archivos WAL en pg_wal y eventualmente se quedará sin espacio. Así que tienes que lidiar con
- Todos los ERROR o cualquier mensaje relacionado con el trabajador lógico que resulte en la salida
- Cuida también de
- wal_receiver_timeout
- wal_sender_timeout
Después de resolver todos los problemas, debería tener su nodo de suscriptor funcionando felizmente. Entonces, la siguiente pregunta es cómo usar esto como un servidor de base de datos de prueba. Tendrá que lidiar con esos problemas/cuestiones:
- Anonimización
- Claves primarias y claves únicas que se basan en violaciones de secuencias
- Un conjunto general de buenas prácticas
- Supervisión
Anonimización
Con respecto a la anonimización de los datos personales que aplica el RGPD en la UE, debe escribir SIEMPRE algunos factores desencadenantes que borren todos los campos relacionados con direcciones, cuentas bancarias, estado civil, números de teléfono, correos electrónicos, etc. Debe consultar con el oficial de seguridad de su empresa sobre qué guardar y qué dejar en blanco. Los activadores deben definirse como SIEMPRE ya que el trabajador lógico ejecuta las instrucciones como REPLICA.
Claves primarias con secuencias
Con respecto a las secuencias, claramente habrá un problema con esas teclas a menos que se solucione antes de comenzar cualquier prueba. Considere este caso:
- El viernes por la tarde realiza algunas pruebas en la base de datos de suscriptores insertando una nueva fila en alguna tabla. Este tendrá como ID el siguiente valor generado por la secuencia.
- Te vas a casa el fin de semana.
- Algún usuario de producción ingresa una fila en la misma tabla en la base de datos del editor.
- La fila se replicará en función de la IDENTIDAD DE LA REPLICA en el nodo del suscriptor, pero fallará debido a un ERROR de infracción de PK. El trabajador de fondo lógico saldrá y volverá a intentarlo. Pero seguirá fallando mientras el problema persista.
- La replicación se atascará. La ranura de replicación comenzará a acumular WAL.
- El editor se queda sin espacio en disco.
- ¡El fin de semana recibe un correo electrónico de que su nodo principal ha entrado en PÁNICO!
Entonces, para resolver el problema de la secuencia, puede adoptar el siguiente enfoque:
select 'SELECT setval(''' || seqrelid::regclass||''','||CASE WHEN seqincrement <0 THEN -214748364 ELSE 214748364 END||');' from pg_sequence where seqtypid=20;
\gexec
Lo que hace lo anterior es establecer secuencias en un valor lo suficientemente grande para que nunca se superpongan en una ventana bastante grande en el futuro, lo que le permite tener un servidor de prueba sin problemas.
Un conjunto de Buenas Prácticas
Realmente debería decirles a sus programadores que hagan que sus pruebas no sean persistentes. Por lo tanto, cualquier prueba después de que se complete debe dejar la base de datos en el mismo estado que tenía antes de la prueba. Con las inserciones de ID basadas en secuencias, esto no es un problema, vimos anteriormente una solución. Pero con claves ÚNICAS que no son de secuencia (por ejemplo, compuestas) eso podría ser un problema. Por lo tanto, es mejor eliminar esos datos de prueba antes de que alguna fila de producción con el mismo valor llegue a la tabla suscrita.
Aquí también deberíamos agregar el manejo de los cambios de esquema. Todos los cambios de esquema deben realizarse también en el suscriptor para no interrumpir el tráfico DML replicado.
Descargue el documento técnico hoy Gestión y automatización de PostgreSQL con ClusterControl Obtenga información sobre lo que necesita saber para implementar, monitorear, administrar y escalar PostgreSQLDescargar el documento técnicoMonitoreo
Realmente debería invertir en una buena solución de monitoreo. Debes monitorear para...
En el suscriptor:
- TODOS los mensajes en el registro del suscriptor que sean relevantes para la salida del trabajador lógico. Instalar una herramienta como tail_n_mail realmente puede ayudar con esto. Una configuración que se sabe que funciona:
Una vez que recibamos una alerta proveniente de tail_n_mail, deberíamos resolver el problema de inmediato.INCLUDE: ERROR: .*publisher.* INCLUDE: ERROR: .*exited with exit.* INCLUDE: LOG: .*exited with exit.* INCLUDE: FATAL: INCLUDE: PANIC:
- pg_stat_subscripción. Pid no debe ser nulo. Además, el retraso debe ser pequeño.
En el editor:
- pg_stat_replication. Esto debería tener tantas filas como se supone que deben ser:una para cada espera de replicación de transmisión conectada (incluidos los nodos de suscripción y otras esperas físicas).
- pg_replication_slots para el espacio del suscriptor. Esto debería estar activo.
Por lo general, toma algún tiempo hasta que tenga su servidor de base de datos de prueba ideal funcionando sin problemas, pero una vez que los haya resuelto todos, ¡sus programadores le agradecerán por tenerlo!