PostgreSQL 16: replicación lógica que ya es práctica
Actualizado: 2026-05-03
PostgreSQL 16, publicado en septiembre de 2023, no trajo una característica que acaparase titulares, sino algo más valioso para quien opera bases de datos en producción: cerró, una a una, las cuatro o cinco grietas que convertían la replicación lógica en un ejercicio de paciencia. Hoy, para migraciones entre versiones, arquitecturas geo-distribuidas con lecturas locales o flujos CDC razonables, la respuesta correcta casi siempre empieza por la replicación lógica incluida en la propia base de datos.
Puntos clave
- Parallel apply en el subscriber: 2-5x de throughput en cargas write-heavy frente a PG15.
- Los standbys físicos pueden exponer slots lógicos propios, descargando al primario.
- La replicación bidireccional añade el andamiaje pero no resuelve conflictos automáticamente: exige disciplina en las claves primarias.
- Los conflictos detienen la suscripción y emiten el LSN exacto:
ALTER SUBSCRIPTION ... SKIPoDISABLE/ENABLEpara resolverlos. - La replicación lógica no replica DDL ni secuencias: plan explícito para cada migración.
Lógica frente a física: el malentendido habitual
La confusión más frecuente es tratar ambos modos como intercambiables. No lo son. La replicación física envía el WAL en crudo a una réplica idéntica byte a byte: es la base de los hot standbys, del failover y de casi toda alta disponibilidad seria. La lógica, en cambio, decodifica ese WAL y lo reexpresa como operaciones de alto nivel —INSERT, UPDATE, DELETE, TRUNCATE— asociadas a una publicación. Esa decodificación tiene un coste de CPU en el origen que la física no paga, pero a cambio permite replicar entre versiones distintas, hacia destinos con esquema diferente, o hacia consumidores que no son siquiera PostgreSQL.
Son herramientas complementarias: nadie debería tirar el standby físico para montarlo todo con lógica.
Las grietas que se cerraron
Parallel apply: hasta PG15 el apply en el subscriber era estrictamente secuencial. Una transacción grande bloqueaba la cola entera durante minutos. PG16 introduce workers paralelos controlados por max_parallel_apply_workers_per_subscription. La mejora típica para cargas write-heavy ronda el 2-5x de throughput.
Slots en standby: antes de PG16, solo el primario podía exponer slots lógicos. A partir de PG16, un standby físico puede exponer sus propios slots y alimentar consumidores downstream, permitiendo arquitecturas de fan-out más limpias: un primario, standbys físicos regionales y, colgando de ellos, los subscribers lógicos de cada región.
Replicación bidireccional: no es multi-master automático. PostgreSQL sigue sin resolver conflictos por ti. Lo que se añade es el andamiaje para construirlo sin extensiones externas, siempre que la aplicación respete particiones de claves primarias que eviten escrituras simultáneas sobre la misma fila en dos nodos. Sin esa disciplina, acabarás con divergencia silenciosa.
Publicaciones, suscripciones y resolución de conflictos
El modelo conceptual sigue siendo el de PG10: en el origen se crea una publicación que declara qué tablas salen, y en el destino una suscripción que apunta a esa publicación:
CREATE PUBLICATION mypub FOR TABLE users, orders;
CREATE SUBSCRIPTION mysub
CONNECTION 'host=primary port=5432 dbname=mydb user=replicator password=secret'
PUBLICATION mypub
WITH (streaming = parallel);
SELECT subname, received_lsn, latest_end_lsn, latest_end_time
FROM pg_stat_subscription;Los conflictos merecen párrafo propio. PG16 no resuelve automáticamente una colisión de clave primaria entre un INSERT local y otro llegado por replicación: detiene la suscripción, la marca como errored y emite el LSN exacto en el log. La operación consiste en decidir manualmente: corregir el origen, saltar la transacción con ALTER SUBSCRIPTION ... SKIP (lsn = ...), o deshabilitar con DISABLE y reactivar tras la reparación. Ver también PostgreSQL 17 y las novedades en vacuum y logical replication para el siguiente paso evolutivo.
Lo que sigue fuera del alcance
Pintar un cuadro realista exige enumerar lo que PG16 no hace:
- No replica DDL: un
ALTER TABLE ADD COLUMNdebe aplicarse manualmente a ambos lados en orden. - Las secuencias no se sincronizan automáticamente: tras un cutover hay que avanzarlas con
setval. - Los large objects (
lo_*) siguen fuera del alcance. TRUNCATEse replica desde PG11 pero arrastra caveats cuando hay claves foráneas entre tablas publicadas y no publicadas.
Para DDL automático, conflict resolution determinista o multi-master genuino, extensiones como pgLogical o soluciones enterprise como BDR siguen siendo la respuesta.
Operación y migraciones
La replicación lógica brilla en dos contextos:
Migración con downtime mínimo entre versiones mayores: estableces la suscripción desde el origen PG14/15 hacia un destino PG16 recién provisionado, dejas converger, pausas la aplicación unos segundos para el último catch-up, conmutas la cadena de conexión y decomisionas el viejo. Minutos de ventana frente a las horas de un pg_dump clásico.
CDC hacia un data warehouse: Debezium se suscribe al slot lógico y empuja los cambios a Kafka; desde ahí alimenta cualquier destino analítico sin tocar la transaccional. Para monitorizar el estado de los slots y el lag de apply, las métricas clave son pg_replication_slot_retained_wal_bytes y el delta entre confirmed_flush_lsn y pg_current_wal_lsn(). Un slot huérfano puede llenar el disco del primario en horas, así que alertar sobre la retención de WAL es prioritario.
PostgreSQL 16 no convierte la replicación lógica en un interruptor mágico; lo que sí hace es eliminar las excusas técnicas que durante años la mantuvieron relegada a casos marginales.