Replicando datos con PostgreSQL 馃悩
Esta semana he estado viendo c贸mo poder sincronizar datos en PostgreSQL sin necesidad de software de terceros.
Hasta ahora no hab铆a tenido la necesidad de hacerlo con PostgreSQL, ya que hasta la fecha estaba usando Microsoft SQL Server y tiene su propio software de replicaci贸n.
PostgreSQL no tiene uno parecido al de MS SQL Server, as铆 que lo he implementado de la siguiente manera.
Lo que hago es que cada vez que se inserte una nueva fila en una tabla que quiero sincronizar, se dispara una funci贸n que coger谩 esa misma fila y la guardar谩 en una tabla de respaldo ubicada en otro servidor.
- Los pasos que seguir茅 ser谩n:
- Crear la tabla de datos de origen
- Crear el servidor remoto en el servidor origen
- Crear una conexi贸n al servidor remoto desde el servidor origen
- Crear una tabla en la base de datos local que se enlazar谩 a otra en el servidor de respaldo o de destino
- Crear la tabla de datos destino en el servidor remoto
- Crear la funci贸n encargada de recibir la nueva fila y hacer la escritura en la tabla de datos replicados
- Crear el disparador que se encargar谩 de llamar a la funci贸n anterior cuando se a帽adan nuevos datos en la tabla de datos de origen.
- Ejecutar pruebas
Replicar datos en bases de datos de PostgreSQL en servidores separados
Para replicar una tabla usar茅 el mecanismo de Foreign Data Wrapper (FDW) que nos permite acceder a datos de una tabla almacenados en un servidor externo como si estuvieran en una tabla en la base de datos local.
驴Por qu茅 uso FDW y no replico la base de datos completa?
Podr铆a replicar toda la base de datos.
La replicaci贸n nos permite crear copias exactas de una base de datos en uno o varios servidores secundarios, que se mantienen actualizadas en tiempo real a medida que se realizan cambios en la base de datos principal.
Para replicar una base de datos en PostgreSQL, se utiliza la herramienta pg_basebackup para crear una copia inicial de la base de datos principal en el servidor secundario, y luego se utiliza la configuraci贸n de replicaci贸n para mantener actualizada la copia en el servidor secundario a medida que se realizan cambios en la base de datos principal.
Una vez que se ha replicado la base de datos en el servidor secundario, se puede acceder a las tablas remotas como si fueran tablas locales en el servidor secundario.
Esto puede ser m谩s r谩pido y eficiente que utilizar FDW, ya que no requiere la transferencia de datos a trav茅s de la red para cada consulta.
No obstante, la replicaci贸n tambi茅n tiene sus desventajas, ya que puede consumir una cantidad significativa de recursos en el servidor principal y secundario, y requiere una configuraci贸n y monitoreo cuidadosos para garantizar la integridad y consistencia de los datos replicados.
Como en este ejemplo s贸lo quiero mantener replicados datos de una sola tabla utilizar茅 el mecanismo de Foreign Data Wrapper (FDW).
Un FDW permite acceder a datos almacenados en un servidor externo como si estuvieran en una tabla local en la base de datos actual.
Para hacer referencia a una tabla remota a trav茅s de un FDW, tendr谩s qu茅:
- Cargar la extensi贸n postgres_fdw
- Crear el servidor remoto
- Crear una conexi贸n al servidor remoto
- Crear una tabla remota en la base de datos local
Una vez creada la tabla remota, podr谩s acceder a ella como si fuera una tabla local utilizando consultas SQL regulares.
馃毄 Antes de comenzar, aseg煤rate que las versiones de PostgreSQL en cada servidor sean compatibles para hacer la conexi贸n.
Arquitectura que usaremos
En el servidor local replicaremos una tabla que se llamara tabla_origen y cada vez que se inserte un nuevo dato se disparar谩 un trigger que se llamar谩 trigger_replica_en_remoto.
El trigger ejecutar谩 una funci贸n llamada funcion_replica_para_servidor_remoto que se encargar谩 en encribir en una tabla local llamada tabla_destino_para_servidor_remoto enlazada con el servidor remoto mediante un foreign server llamado server_13_2.
En el servidor remoto s贸lo tendremos una tabla llamada tabla_destino_en_servidor_remoto con la misma estructura que la tabla tabla_origen.
Cargar la extensi贸n postgres_fdw
Para habilitar el conector de datos externos, en ambos servidores principal y de respaldo, ejecutar:
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
Crear servidor remoto de respaldo en servidor local
Crearemos una especie de servidor PostgreSQL virtual en nuestro servidor local
CREATE SERVER server_13_2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'ip_servidor', port 'puerto', dbname 'nombre_base_de_datos');
Creando usuario para la conexi贸n con servidor remoto de respaldo
CREATE USER MAPPING FOR usuario_con_el_que_te_quieres_conectar SERVER server_13_2 OPTIONS (user 'user', password 'password');
Crear tabla destino en servidor remoto
En el servidor remoto o de respaldo creo la tabla que recibir谩 los datos del servidor principal o de origen.
//TABLA DESTINO EN SERVIDOR REMOTO CREATE TABLE public.tabla_destino_en_servidor_remoto ( id integer, description character varying )
馃毄 ten en cuenta que el usuario de PostgreSQL que vaya a escribir desde el servidor origen tenga permisos para escribir aqu铆, en la tabla destino del servidor remoto.
Crear tabla para servidor remoto
Creo la tabla en el servidor principal o de origen que enlazar谩 los datos con el servidor de respaldo o de destino.
CREATE FOREIGN TABLE tabla_destino_para_servidor_remoto (id integer, description character varying) SERVER server_13_2 OPTIONS (schema_name 'public', table_name 'tabla_destino_en_servidor_remoto');
Crear la funci贸n encargada de escribir los nuevos datos en la tabla para servidor remoto
Creamos una funci贸n a la que llamaremos funcion_replica_para_servidor_remoto.
Entre las palabras reservadas BEGIN y END se escribir谩 el c贸digo de lo que se se quiera ejecutar, en nuestro caso haremos un INSERT a la tabla tabla_destino_para_servidor_remoto (creada en el punto anterior) de los nuevos valores que vienen en la variable NEW
CREATE FUNCTION funcion_replica_para_servidor_remoto() RETURNS trigger AS $$ BEGIN INSERT INTO tabla_destino_para_servidor_remoto VALUES (NEW.*); RETURN NEW; END $$ LANGUAGE plpgsql;
Crear el trigger que llamar谩 a la funci贸n de replicar los datos
Crearemos un trigger o disparador que se llamar谩 trigger_replica_en_remoto que se disparar谩 cada vez que se inserte un dato en la tabla tabla_origen y ser谩 el encargado de ejecutar la funci贸n programada en el punto anterior funcion_replica_para_servidor_remoto
CREATE TRIGGER trigger_replica_en_remoto AFTER INSERT ON tabla_origen FOR EACH ROW EXECUTE FUNCTION funcion_replica_para_servidor_remoto();
Pruebas
Para probar que todo est谩 correcto a帽ade datos a la tabla_origen, yo a帽adir茅 una fila con el id = 3 y el description = test para remoto 3
INSERT INTO tabla_origen (id, description) VALUES (3, 'test para remoto 3');
Ahora compruebo que los datos se hayan replicado en el servidor remoto o de respaldo.
BOOM! 馃挘, disfr煤talo.
Errores y soluciones
Error de permiso para escribir en la tabla for谩nea
Un escenario en el que tuve problemas fue con una aplicaci贸n de Java que escrib铆a en PostgreSQL, pero a la hora de activar la sincronizaci贸n como te la he explicado no hac铆a los INSERT, el error que me arrojaba era:
[ERROR] com.inerco.tse.bd.BdAcceso -> insertaRegistros() -> Error al insertar
### Error updating database. Cause: org.postgresql.util.PSQLException: ERROR: permiso denegado a la tabla for谩nea tb_gas_server80 Where: sentencia SQL: 芦INSERT INTO tb_gas_server80 VALUES (NEW.*)禄 funci贸n PL/pgSQL replica_tb_gas_para_server80() en la l铆nea 3 en sentencia SQL
El tema era que cuando yo hac铆a el INSERT desde el PgAdmin, s铆 lo hac铆a bien, y era porque yo hac铆a en INSERT logueado con el usuario postgres en PgAmin.
Cuando me logue茅 en PgAdmin con el usuario que intentaba acceder el programa de Java ya consegu铆 que me saliera el error desde PgAdmin.
Si quieres saber c贸mo loguearte en PgAmin con un usuario distinto ve aqu铆
Para solventar este problema lo que hice fue asignar al usuario con el que acced铆a el programa de Java como propietario de las tablas implicadas en la r茅plica, tanto en el servidor origen como en el remoto.
ALTER TABLE nombre_tabla OWNER TO nuevo_propietario;
Error de mapeo de usuario
Te puedes encontrar con el siguiente error:
ERROR: no se encontr贸 un mapeo para el usuario 芦usuario_en_cuestion禄 CONTEXT: sentencia SQL: 芦INSERT INTO tb_gas_server80 VALUES (NEW.*)禄 funci贸n PL/pgSQL replica_gas() en la l铆nea 3 en sentencia SQL SQL state: 42704
Para solventarlo, a帽ade al usuario para que tenga acceso al servidor.
CREATE USER MAPPING FOR usuario_en_cuestion SERVER server_13_2 OPTIONS (user 'user', password 'password');
驴Sincronizar datos en una misma base de datos de PostgreSQL?
Visto lo anterior, hacerlo en local ser谩 mucho m谩s f谩cil.
Crear las tablas de origen y de replicaci贸n
Creamos las tablas, las llamaremos tabla_origen y tabla_replicada. Cada una de las tablas con un campo id num茅rico y un campo description de texto.
//TABLA ORIGEN CREATE TABLE public.tabla_origen ( id integer, description character varying ) //TABLA PARA DATOS REPLICADOS CREATE TABLE public.tabla_replicada ( id integer, description character varying )
Crear la funci贸n encargada de escribir los nuevos datos en la tabla de replicaci贸n
Creamos una funci贸n a la que llamaremos funcion_replica_origen.
Entre las palabras reservadas BEGIN y END se escribir谩 el c贸digo de lo que se quiera ejecutar. En nuestro caso haremos un INSERT a la tabla tabla_replicada de los nuevos valores que vienen en la variable NEW
CREATE FUNCTION funcion_replica_origen() RETURNS trigger AS $$ BEGIN INSERT INTO tabla_replicada VALUES (NEW.*); RETURN NEW; END $$ LANGUAGE plpgsql;
Crear el trigger que llamar谩 a la funci贸n de replicar los datos
Crearemos un trigger o disparador que se llamar谩 trigger_replica_origen que se disparar谩 cada vez que se inserte un dato en la tabla tabla_origen y ser谩 el encargado de ejecutar la funci贸n programada en el punto anterior funcion_replica_origen
CREATE TRIGGER trigger_replica_origen AFTER INSERT ON tabla_origen FOR EACH ROW EXECUTE FUNCTION funcion_replica_origen();
Pruebas dentro de la misma base de datos
Para probar que todo est谩 correcto a帽ade datos a la tabla_origen
INSERT INTO tabla_origen (id, description) VALUES (1, 'test1');
Revisa que se han a帽adido los datos tanto a tabla_origen como a tabla_replicada
SELECT * FROM public.tabla_origen SELECT * FROM public.tabla_replicada
驴C贸mo s茅 qu茅 versi贸n de PostgreSQL estoy usando?
Para saber qu茅 versi贸n de PostgreSQL tienes ejecuta la instrucci贸n SQL en tu servidor:
SELECT version();
驴C贸mo iniciar sesi贸n en PgAdmin con otro usuario?
Para loguearte en PgAdmin con un usuario distinto:
- Descon茅ctate del servidor
- Con el servidor desconectado haz clic con el bot贸n derecho y pulsa en propiedades
- En la pesta帽a de Connection cambia el Username con el que quiere entrar
Hasta luego 馃枛