{"id":6832,"date":"2026-05-03T11:43:47","date_gmt":"2026-05-03T09:43:47","guid":{"rendered":"https:\/\/rootfan.com\/?p=6832"},"modified":"2026-05-06T11:14:05","modified_gmt":"2026-05-06T09:14:05","slug":"replicacion-de-oracle-a-postgresql-debezium","status":"publish","type":"post","link":"https:\/\/rootfan.com\/es\/oracle-to-postgresql-replication-debezium\/","title":{"rendered":"C\u00f3mo configurar la replicaci\u00f3n en tiempo real de Oracle a PostgreSQL usando Debezium"},"content":{"rendered":"<p class=\"wp-block-paragraph\" id=\"tl-dr\"><strong>En resumen<\/strong> Debezium lee los registros de rehacer de Oracle a trav\u00e9s de LogMiner, publica cada cambio como un evento de Kafka y un conector JDBC de destino aplica esos eventos a PostgreSQL en tiempo real.<br>El resultado es un pipeline de replicaci\u00f3n transparente y reproducible que puedes ejecutar durante semanas antes de la migraci\u00f3n, \u00fatil para cualquier migraci\u00f3n de Oracle a PostgreSQL que requiera tiempo de inactividad cero o cercano a cero.<br>Esta publicaci\u00f3n repasa una configuraci\u00f3n de extremo a extremo que funciona, en servidores Oracle y PostgreSQL reales, con un esquema bancario aut\u00f3nomo que puede copiar y reproducir.<\/p>\n\n\n\n<!--more-->\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">La mayor\u00eda de los proyectos de migraci\u00f3n de Oracle a PostgreSQL fallan en el corte, no en la conversi\u00f3n del esquema.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El trabajo de esquema es dif\u00edcil pero acotado: dedicas unas semanas a \u00e9l, corriges lo que la herramienta de conversi\u00f3n no puede traducir y sigues adelante.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La transici\u00f3n est\u00e1 abierta.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tienes una base de datos Oracle en producci\u00f3n que procesa miles de transacciones por segundo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Necesita cambiar la aplicaci\u00f3n a PostgreSQL sin perder ni una sola fila y, idealmente, sin una ventana de inactividad de varias horas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Una migraci\u00f3n basada en instant\u00e1neas por s\u00ed sola \u2014volcar, restaurar, redirigir\u2014 no resuelve esto.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En el momento en que tomas la instant\u00e1nea, cada nuevo INSERT, UPDATE y DELETE en Oracle es invisible para PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Necesitas una herramienta que capture los cambios continuos de Oracle y los aplique a PostgreSQL de forma continua.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium es el est\u00e1ndar de c\u00f3digo abierto para esa tarea.<\/p>\n\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>\u00cdndice<\/h2><nav><ul><li><a href=\"#what-debezium-does\">Lo que hace Debezium<\/a><\/li><li><a href=\"#the-lab-environment\">El medioambiente<\/a><\/li><li><a href=\"#step-1-prepare-the-oracle-source\">Paso 1 \u2014 Preparar la fuente de Oracle<\/a><\/li><li><a href=\"#step-2-create-the-banking-schema\">Paso 2 \u2014 Cree el esquema BANKING<\/a><\/li><li><a href=\"#step-3-start-kafka-and-kafka-connect\">Paso 3: Iniciar Kafka y Kafka Connect<\/a><\/li><li><a href=\"#step-4-register-the-oracle-source-connector\">Paso 4: Registrar el conector de origen Oracle<\/a><\/li><li><a href=\"#step-5-watch-cdc-events-live\">Paso 5 \u2014 Ver eventos de los CDC en vivo<\/a><\/li><li><a href=\"#step-6-set-up-the-postgre-sql-sink\">Paso 6: Configurar el Target de PostgreSQL<\/a><\/li><li><a href=\"#step-7-add-a-new-table-mid-stream\">Paso 7: Agregar una nueva tabla en medio del flujo<\/a><\/li><li><a href=\"#the-schema-evolution-tradeoff\">La compensaci\u00f3n del esquema.evoluci\u00f3n<\/a><\/li><li><a href=\"#the-new-table-discovery-delay\">El retraso en el descubrimiento de la nueva tabla<\/a><\/li><li><a href=\"#production-gotchas\">Problemas en la producci\u00f3n<\/a><\/li><li><a href=\"#frequently-asked-questions\">Preguntas frecuentes<\/a><ul><li><a href=\"#faq-question-1777800041851\">\u00bfPuede Debezium migrar el esquema de Oracle, o solo los datos?<\/a><\/li><li><a href=\"#faq-question-1777800042851\">\u00bfCu\u00e1l es la latencia entre Oracle y PostgreSQL?<\/a><\/li><li><a href=\"#faq-question-1777800043851\">\u00bfEst\u00e1 LogMiner obsoleto en Oracle 19c o 21c?<\/a><\/li><li><a href=\"#faq-question-1777800044851\">\u00bfEsto puede ejecutarse contra Oracle Standard Edition?<\/a><\/li><li><a href=\"#faq-question-1777800045851\">\u00bfPuedo usar Debezium para migraci\u00f3n de un solo disparo sin mantenerlo en ejecuci\u00f3n?<\/a><\/li><\/ul><\/li><li><a href=\"#in-summary\">En resumen<\/a><\/li><\/ul><\/nav><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-debezium-does\">Lo que hace Debezium<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium es un framework de captura de datos de cambio (CDC) construido sobre Kafka Connect.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para Oracle, utiliza el adaptador LogMiner, una caracter\u00edstica integrada de Oracle que lee los registros de rehacer y expone cada cambio confirmado como una transmisi\u00f3n estructurada.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium consulta LogMiner, convierte cada cambio de fila en un evento de Kafka y publica el evento en un tema de Kafka.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un segundo conector \u2014 el sink JDBC de Debezium \u2014 se suscribe a esos temas y aplica los cambios a una base de datos de destino.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para la replicaci\u00f3n de Oracle a PostgreSQL, el pipeline es el siguiente:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Oracle (LogMiner) \u2192 Debezium fuente \u2192 Kafka \u2192 Debezium JDBC sink \u2192 PostgreSQL<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ambos conectores se ejecutan dentro de un \u00fanico proceso de Debezium Connect.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Kafka mantiene eventos en temas con nombres que corresponden a cada tabla de Oracle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Puedes detener el \"sink\" y dejar que los eventos se acumulen en Kafka.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Puedes reproducir eventos desde un desplazamiento espec\u00edfico.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Puedes ejecutar dos \"sinks\" en paralelo: uno a PostgreSQL y otro a un entorno de staging para pruebas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esto es lo que hace que Debezium sea \u00fatil para el trabajo de migraci\u00f3n: puedes ver exactamente lo que est\u00e1 sucediendo en cada etapa y puedes intervenir en cualquiera de ellas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-lab-environment\">El medioambiente<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Tres VMs en la misma red.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>srv1<\/code> se ejecuta Oracle 19c Enterprise Edition con un CDB llamado <code>ORADB<\/code> y una base de datos conectable <code>pdb1<\/code>.<\/li>\n\n\n\n<li><code>srvdebezium<\/code> (192.168.0.230) ejecuta Ubuntu 24.04 con Docker, alojando el broker de Kafka y el contenedor Debezium Connect.<\/li>\n\n\n\n<li><code>srv2<\/code> (192.168.0.181) ejecuta PostgreSQL 18 del repositorio oficial PGDG, alojando la base de datos de destino <code>bankingdb<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">La canalizaci\u00f3n replica un esquema bancario peque\u00f1o de Oracle a PostgreSQL: cinco tablas (<code>ramas<\/code>, <code>empleados<\/code>, <code>clientes<\/code>, <code>cuentas<\/code>, <code>transacciones<\/code>) con relaciones de clave for\u00e1nea, tipos de datos mixtos y 18 filas de ejemplo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El DDL completo y los datos de ejemplo se encuentran a continuaci\u00f3n: copie y pegue reproducible desde una instalaci\u00f3n limpia de Oracle.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-1-prepare-the-oracle-source\">Paso 1 \u2014 Preparar la fuente de Oracle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">LogMiner necesita tres cosas activadas antes de que Debezium pueda leerlo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Modo archivelog.<\/strong><br>Sin \u00e9l, Oracle sobrescribe los registros de rehacer antiguos una vez que ya no son necesarios para la recuperaci\u00f3n de instancias.<br>LogMiner los necesita en disco para leerlos.<br>Cambiar al modo archivelog requiere un reinicio de la instancia.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- On srv1 as SYSDBA\nSHUTDOWN IMMEDIATE;\nSTARTUP MOUNT;\nALTER DATABASE ARCHIVELOG;\nALTER DATABASE OPEN;\nALTER PLUGGABLE DATABASE pdb1 OPEN;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\"><strong>Registro adicional.<\/strong><br>Por defecto, Oracle escribe solo las columnas modificadas en el registro de rehacer.<br>Debezium necesita im\u00e1genes completas de fila para poblar <code>antes<\/code> campo en eventos de actualizaci\u00f3n.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nALTER DATABASE ADD SUPPLEMENTAL LOG DATA;\nALTER DATABASE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\"><strong>Capturar usuario.<\/strong><br>Debezium se conecta como un usuario com\u00fan (el <code>C##<\/code> (el prefijo es obligatorio en una base de datos multiinquilina) con acceso de lectura al diccionario de datos y a las vistas de LogMiner.<br>Aplica todas las subvenciones; Debezium fallar\u00e1 al iniciarse si falta alguna.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- On srv1, as SYSDBA, connected to the CDB root\n\nCREATE USER C##DBZUSER IDENTIFIED BY dbz\n  DEFAULT TABLESPACE users\n  QUOTA UNLIMITED ON users\n  CONTAINER=ALL;\n\n-- Session grants\nGRANT CREATE SESSION TO C##DBZUSER CONTAINER=ALL;\nGRANT SET CONTAINER  TO C##DBZUSER CONTAINER=ALL;\n\n-- LogMiner privilege \u2014 required to start and use LogMiner sessions\nGRANT LOGMINING TO C##DBZUSER CONTAINER=ALL;\n\n-- Transaction and dictionary access\nGRANT SELECT ANY TRANSACTION TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ANY DICTIONARY  TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT_CATALOG_ROLE    TO C##DBZUSER CONTAINER=ALL;\nGRANT EXECUTE_CATALOG_ROLE   TO C##DBZUSER CONTAINER=ALL;\n\n-- Flashback and table access \u2014 needed for the initial snapshot\nGRANT FLASHBACK ANY TABLE TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ANY TABLE    TO C##DBZUSER CONTAINER=ALL;\n\n-- Object creation \u2014 Debezium creates internal tracking objects in its own schema\nGRANT CREATE TABLE    TO C##DBZUSER CONTAINER=ALL;\nGRANT LOCK ANY TABLE  TO C##DBZUSER CONTAINER=ALL;\nGRANT CREATE SEQUENCE TO C##DBZUSER CONTAINER=ALL;\n\n-- LogMiner package execution\nGRANT EXECUTE ON DBMS_LOGMNR   TO C##DBZUSER CONTAINER=ALL;\nGRANT EXECUTE ON DBMS_LOGMNR_D TO C##DBZUSER CONTAINER=ALL;\n\n-- V$ views \u2014 Debezium queries these to track log position, archive status, and transactions\nGRANT SELECT ON V_$DATABASE             TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$LOG                  TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$LOG_HISTORY          TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$LOGFILE              TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$ARCHIVED_LOG         TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$ARCHIVE_DEST_STATUS  TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$LOGMNR_LOGS          TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$LOGMNR_CONTENTS      TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$LOGMNR_PARAMETERS    TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$TRANSACTION          TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$MYSTAT               TO C##DBZUSER CONTAINER=ALL;\nGRANT SELECT ON V_$STATNAME             TO C##DBZUSER CONTAINER=ALL;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">No acortes esto con <code>OTORGAR DBA<\/code> \u2014 las concesiones expl\u00edcitas son auditables, reversibles y coinciden exactamente con la documentaci\u00f3n de Debezium.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-2-create-the-banking-schema\">Paso 2 \u2014 Cree el esquema BANKING<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">El esquema a continuaci\u00f3n es lo suficientemente peque\u00f1o como para pegarlo en 30 segundos y lo suficientemente variado como para ejercitar el comportamiento del tipo de datos que Debezium acierta y falla.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- On srv1, connected to pdb1 as a privileged user\nCREATE USER banking IDENTIFIED BY banking\n  DEFAULT TABLESPACE users\n  TEMPORARY TABLESPACE temp;\nGRANT CONNECT, RESOURCE, CREATE VIEW TO banking;\nALTER USER banking QUOTA UNLIMITED ON users;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Conectar como <code>banca<\/code> y crea las cinco tablas:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE TABLE branches (\n  branch_id    NUMBER(6) PRIMARY KEY,\n  branch_name  VARCHAR2(100) NOT NULL,\n  city         VARCHAR2(50),\n  country      VARCHAR2(50),\n  opened_date  DATE\n);\n\nCREATE TABLE employees (\n  employee_id  NUMBER(6) PRIMARY KEY,\n  branch_id    NUMBER(6) NOT NULL,\n  full_name    VARCHAR2(100) NOT NULL,\n  role         VARCHAR2(50),\n  hire_date    DATE,\n  CONSTRAINT fk_emp_branch FOREIGN KEY (branch_id) REFERENCES branches(branch_id)\n);\n\nCREATE TABLE customers (\n  customer_id  NUMBER(8) PRIMARY KEY,\n  full_name    VARCHAR2(100) NOT NULL,\n  email        VARCHAR2(100),\n  country      VARCHAR2(50),\n  created_at   TIMESTAMP DEFAULT SYSTIMESTAMP\n);\n\nCREATE TABLE accounts (\n  account_id    NUMBER(10) PRIMARY KEY,\n  customer_id   NUMBER(8) NOT NULL,\n  branch_id     NUMBER(6) NOT NULL,\n  account_type  VARCHAR2(20),\n  balance       NUMBER(15,2) DEFAULT 0,\n  opened_at     TIMESTAMP DEFAULT SYSTIMESTAMP,\n  CONSTRAINT fk_acc_customer FOREIGN KEY (customer_id) REFERENCES customers(customer_id),\n  CONSTRAINT fk_acc_branch   FOREIGN KEY (branch_id)   REFERENCES branches(branch_id)\n);\n\nCREATE TABLE transactions (\n  txn_id       NUMBER(12) PRIMARY KEY,\n  account_id   NUMBER(10) NOT NULL,\n  employee_id  NUMBER(6),\n  txn_type     VARCHAR2(20),\n  amount       NUMBER(15,2) NOT NULL,\n  txn_date     TIMESTAMP DEFAULT SYSTIMESTAMP,\n  description  VARCHAR2(200),\n  CONSTRAINT fk_txn_account  FOREIGN KEY (account_id)  REFERENCES accounts(account_id),\n  CONSTRAINT fk_txn_employee FOREIGN KEY (employee_id) REFERENCES employees(employee_id)\n);\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Inserte un conjunto de datos representativo peque\u00f1o \u2014 tres sucursales, cuatro empleados, tres clientes, cuatro cuentas, cuatro transacciones:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- BRANCHES\nINSERT INTO branches VALUES (1, &#039;Madrid Centro&#039;,      &#039;Madrid&#039;,    &#039;Spain&#039;,    DATE &#039;2010-03-15&#039;);\nINSERT INTO branches VALUES (2, &#039;Barcelona Diagonal&#039;, &#039;Barcelona&#039;, &#039;Spain&#039;,    DATE &#039;2012-06-01&#039;);\nINSERT INTO branches VALUES (3, &#039;Lisbon Avenida&#039;,     &#039;Lisbon&#039;,    &#039;Portugal&#039;, DATE &#039;2015-09-10&#039;);\n\n-- EMPLOYEES\nINSERT INTO employees VALUES (101, 1, &#039;Maria Lopez&#039;,   &#039;Branch Manager&#039;,  DATE &#039;2015-01-15&#039;);\nINSERT INTO employees VALUES (102, 1, &#039;Carlos Diaz&#039;,   &#039;Teller&#039;,          DATE &#039;2018-03-22&#039;);\nINSERT INTO employees VALUES (103, 2, &#039;Anna Pereira&#039;,  &#039;Branch Manager&#039;,  DATE &#039;2016-07-01&#039;);\nINSERT INTO employees VALUES (104, 3, &#039;Pedro Santos&#039;,  &#039;Account Officer&#039;, DATE &#039;2020-11-05&#039;);\n\n-- CUSTOMERS\nINSERT INTO customers VALUES (1001, &#039;John Smith&#039;,    &#039;john@example.com&#039;,   &#039;UK&#039;,      SYSTIMESTAMP);\nINSERT INTO customers VALUES (1002, &#039;Hans Mueller&#039;,  &#039;hans@example.com&#039;,   &#039;Germany&#039;, SYSTIMESTAMP);\nINSERT INTO customers VALUES (1003, &#039;Sophie Dubois&#039;, &#039;sophie@example.com&#039;, &#039;France&#039;,  SYSTIMESTAMP);\n\n-- ACCOUNTS\nINSERT INTO accounts VALUES (10001, 1001, 1, &#039;CHECKING&#039;,  5000.00,  SYSTIMESTAMP);\nINSERT INTO accounts VALUES (10002, 1001, 1, &#039;SAVINGS&#039;,   25000.00, SYSTIMESTAMP);\nINSERT INTO accounts VALUES (10003, 1002, 2, &#039;CHECKING&#039;,  12000.00, SYSTIMESTAMP);\nINSERT INTO accounts VALUES (10004, 1003, 3, &#039;CHECKING&#039;,  8500.00,  SYSTIMESTAMP);\n\n-- TRANSACTIONS\nINSERT INTO transactions VALUES (100001, 10001, 102, &#039;DEPOSIT&#039;,    1500.00, SYSTIMESTAMP, &#039;Salary deposit&#039;);\nINSERT INTO transactions VALUES (100002, 10001, 102, &#039;WITHDRAWAL&#039;, 200.00,  SYSTIMESTAMP, &#039;ATM withdrawal&#039;);\nINSERT INTO transactions VALUES (100003, 10003, 103, &#039;DEPOSIT&#039;,    3000.00, SYSTIMESTAMP, &#039;Cash deposit&#039;);\nINSERT INTO transactions VALUES (100004, 10004, 104, &#039;TRANSFER&#039;,   500.00,  SYSTIMESTAMP, &#039;Transfer to savings&#039;);\n\nCOMMIT;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Dieciocho filas en total, todas las claves for\u00e1neas satisfechas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Este es el estado fuente que Debezium capturar\u00e1.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-3-start-kafka-and-kafka-connect\">Paso 3: Iniciar Kafka y Kafka Connect<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En <code>srvdebezium<\/code>, crea una red de Docker e inicia Kafka en modo detached para que el broker sobreviva al cierre de la terminal.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\ndocker network create debezium-net\n\ndocker run -d --name kafka --hostname kafka --network debezium-net \\\n  -p 9092:9092 \\\n  -e CLUSTER_ID=$(docker run --rm quay.io\/debezium\/kafka:3.5 \/kafka\/bin\/kafka-storage.sh random-uuid) \\\n  -e NODE_ID=1 -e NODE_ROLE=combined \\\n  -e KAFKA_CONTROLLER_QUORUM_VOTERS=1@kafka:9093 \\\n  -e KAFKA_LISTENERS=PLAINTEXT:\/\/0.0.0.0:9092,CONTROLLER:\/\/0.0.0.0:9093 \\\n  -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT:\/\/kafka:9092 \\\n  quay.io\/debezium\/kafka:3.5\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">El conector Debezium para Oracle se env\u00eda sin el controlador JDBC de Oracle por motivos de licencia.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Descargar <code>ojdbc11.jar<\/code> desde Maven Central y montarlo en Connect al iniciar:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nmkdir -p \/home\/fernando\/oracle-jdbc\ncurl -L \\\n  https:\/\/repo1.maven.org\/maven2\/com\/oracle\/database\/jdbc\/ojdbc11\/21.9.0.0\/ojdbc11-21.9.0.0.jar \\\n  -o \/home\/fernando\/oracle-jdbc\/ojdbc11.jar\n\ndocker run -d --name connect --network debezium-net \\\n  -p 8083:8083 \\\n  -e BOOTSTRAP_SERVERS=kafka:9092 \\\n  -e GROUP_ID=1 \\\n  -e CONFIG_STORAGE_TOPIC=my_connect_configs \\\n  -e OFFSET_STORAGE_TOPIC=my_connect_offsets \\\n  -e STATUS_STORAGE_TOPIC=my_connect_statuses \\\n  -v \/home\/fernando\/oracle-jdbc\/ojdbc11.jar:\/kafka\/connect\/debezium-connector-oracle\/ojdbc11.jar \\\n  quay.io\/debezium\/connect:3.5\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Utilice <code>-d<\/code> (desprendido).<br>Sin \u00e9l, cerrar la terminal detiene el contenedor y detiene su pipeline de replicaci\u00f3n.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Si est\u00e1 planificando una migraci\u00f3n de Oracle a PostgreSQL y desea una segunda opini\u00f3n sobre el dise\u00f1o del corte, <a href=\"https:\/\/rootfan.com\/es\/servicios\/\">Ofrezco una evaluaci\u00f3n de migraci\u00f3n de tarifa fija \u2192<\/a><\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-4-register-the-oracle-source-connector\">Paso 4: Registrar el conector de origen Oracle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La configuraci\u00f3n del conector le indica a Debezium qu\u00e9 base de datos leer, qu\u00e9 esquema capturar y d\u00f3nde almacenar su historial de esquemas interno.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\ncurl -i -X POST -H &quot;Content-Type:application\/json&quot; \\\n  http:\/\/localhost:8083\/connectors\/ -d &#039;{\n    &quot;name&quot;: &quot;oracle-connector&quot;,\n    &quot;config&quot;: {\n      &quot;connector.class&quot;: &quot;io.debezium.connector.oracle.OracleConnector&quot;,\n      &quot;tasks.max&quot;: &quot;1&quot;,\n      &quot;database.hostname&quot;: &quot;192.168.0.180&quot;,\n      &quot;database.port&quot;: &quot;1521&quot;,\n      &quot;database.user&quot;: &quot;C##DBZUSER&quot;,\n      &quot;database.password&quot;: &quot;dbz&quot;,\n      &quot;database.dbname&quot;: &quot;ORADB&quot;,\n      &quot;database.pdb.name&quot;: &quot;pdb1&quot;,\n      &quot;topic.prefix&quot;: &quot;oracle&quot;,\n      &quot;schema.include.list&quot;: &quot;BANKING&quot;,\n      &quot;schema.history.internal.kafka.bootstrap.servers&quot;: &quot;kafka:9092&quot;,\n      &quot;schema.history.internal.kafka.topic&quot;: &quot;schemahistory.oracle&quot;,\n      &quot;decimal.handling.mode&quot;: &quot;precise&quot;,\n      &quot;datatype.propagate.source.type&quot;: &quot;VARCHAR2,NUMBER,CHAR,NCHAR,NVARCHAR2&quot;\n    }\n  }&#039;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Cuatro ajustes que vale la pena entender:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>schema.include.list: \"BANCA\"<\/code> captura todas las tablas en el esquema BANKING.<br>No <code>tabla.incluir.lista<\/code> se configura, por lo que cuando aparece una nueva tabla en BANKING, Debezium la detecta autom\u00e1ticamente; ver\u00e1s esto en el Paso 7.<\/li>\n\n\n\n<li><code>tema.prefijo: \"oracle\"<\/code> controla el nombre del tema de Kafka.<br>Eventos para <code>BANCA.TRANSACCIONES<\/code> aterrizar en <code>oracle.BANCARIO.TRANSACCIONES<\/code>.<br>El nombre de la PDB no aparece en el nombre del tema, aunque <code>nombre.de.la.base.de.datos<\/code> se establece; se usa para filtrar \u00fanicamente.<\/li>\n\n\n\n<li><code>decimal.handling.mode: \"precise\"<\/code> hace Oracle <code>N\u00daMERO<\/code> los valores llegan como Java <code>BigDecimal<\/code> en los eventos de Kafka, preservando la precisi\u00f3n y la escala. El sink JDBC usa esto para crear <code>num\u00e9rico(p,s)<\/code>, <code>entero largo<\/code>y <code>entero<\/code> columnas en PostgreSQL.<\/li>\n\n\n\n<li><code>datatype.propagate.source.type: \"VARCHAR2,NUMBER,CHAR,NCHAR,NVARCHAR2\"<\/code> incrusta la declaraci\u00f3n de columna original de Oracle en el esquema de cada evento como metadatos. Sin ella, el sink solo ve el tipo JDBC. <code>CADENA<\/code> para cada columna \u2014 y crea <code>texto<\/code> para todo, incluidas las columnas num\u00e9ricas.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">El conector inicia inmediatamente una fase de instant\u00e1nea: escanea cada tabla capturada, publica cada fila existente como un evento con tipo de operaci\u00f3n <code>r<\/code> (lee) y luego cambia al modo de transmisi\u00f3n donde sigue el registro de rehacer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Puedes ver el progreso con <code>docker logs -f connect<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-5-watch-cdc-events-live\">Paso 5 \u2014 Ver eventos de los CDC en vivo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Abre una segunda terminal en <code>srvdebezium<\/code> y iniciar un consumidor de consola de Kafka para observar los eventos a medida que llegan.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tuber\u00eda a trav\u00e9s de <code>grep<\/code> para extraer solo los campos significativos \u2014 el sobre JSON completo est\u00e1 dominado por la definici\u00f3n del esquema, lo cual est\u00e1 bien para el procesamiento pero es ilegible en pantalla.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\ndocker run -it --rm --name watcher --network debezium-net \\\n  --entrypoint \/kafka\/bin\/kafka-console-consumer.sh \\\n  quay.io\/debezium\/kafka:3.5 \\\n  --bootstrap-server kafka:9092 \\\n  --topic oracle.BANKING.TRANSACTIONS \\\n  --from-beginning 2&gt;\/dev\/null \\\n  | grep -oE &#039;&quot;(op|TXN_ID|TXN_TYPE|AMOUNT|DESCRIPTION)&quot;:&quot;&#x5B;^&quot;]*&quot;&#039;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Aparecen inmediatamente cuatro eventos de instant\u00e1nea, uno por cada transacci\u00f3n existente, cada uno con <code>\"op\":\"r\"<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ahora genera un cambio en vivo en Oracle:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nINSERT INTO transactions VALUES (100005, 10002, 102, &#039;DEPOSIT&#039;, 5000.00, SYSTIMESTAMP, &#039;Bonus deposit&#039;);\nCOMMIT;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Un nuevo evento aparece con <code>\"op\":\"c\"<\/code> y la fila completa en <code>despu\u00e9s<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Una ACTUALIZACI\u00d3N muestra <code>\"op\":\"u\"<\/code> con ambos <code>antes<\/code> y <code>despu\u00e9s<\/code> poblado.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un DELETE muestra <code>\"op\":\"d\"<\/code> con <code>antes<\/code> poblado y <code>despu\u00e9s<\/code> nulo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Latencia sub-segundo en estado estacionario.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-6-set-up-the-postgre-sql-sink\">Paso 6: Configurar el Target de PostgreSQL<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En <code>srv2<\/code>, crea el usuario de destino, la base de datos y el esquema, y permite conexiones remotas desde <code>srvdebezium<\/code>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nsudo -u postgres psql -c &quot;CREATE USER banking WITH PASSWORD &#039;banking&#039;;&quot;\nsudo -u postgres psql -c &quot;CREATE DATABASE bankingdb OWNER banking;&quot;\nsudo -u postgres psql -d bankingdb -c &quot;CREATE SCHEMA banking AUTHORIZATION banking;&quot;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Editar los dos archivos de configuraci\u00f3n de PostgreSQL en <code>srv2<\/code>.\n<code>listen_addresses = '*'<\/code> le dice a PostgreSQL que acepte conexiones en todas las interfaces de red, no solo en localhost.\nEl <code>pg_hba.conf<\/code> l\u00ednea permite a cualquier usuario y base de datos desde el <code>192.168.0.0\/24<\/code> subred para autenticarse con SCRAM (el protocolo seguro de contrase\u00f1as cifradas), que es c\u00f3mo el conector sink JDBC de Debezium acceder\u00e1 a la base de datos.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# \/etc\/postgresql\/18\/main\/postgresql.conf\nlisten_addresses = &#039;*&#039;\n\n# \/etc\/postgresql\/18\/main\/pg_hba.conf\nhost    all    all    192.168.0.0\/24    scram-sha-256\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nsudo systemctl restart postgresql\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">En <code>srvdebezium<\/code>, registra el conector JDBC sink.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En <code>RegexRouter<\/code> SMT despoja <code>oracle.BANCA.<\/code> prefijo de cada tema antes de que se convierta en el nombre de la tabla de destino, por lo que <code>oracle.BANCARIO.TRANSACCIONES<\/code> mapas a <code>banca.transacciones<\/code> en lugar de <code>banca.oracle_banca_transacciones<\/code>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\ncurl -i -X POST -H &quot;Content-Type:application\/json&quot; \\\n  http:\/\/localhost:8083\/connectors\/ -d &#039;{\n    &quot;name&quot;: &quot;jdbc-sink-connector&quot;,\n    &quot;config&quot;: {\n      &quot;connector.class&quot;: &quot;io.debezium.connector.jdbc.JdbcSinkConnector&quot;,\n      &quot;tasks.max&quot;: &quot;1&quot;,\n      &quot;connection.url&quot;: &quot;jdbc:postgresql:\/\/192.168.0.181:5432\/bankingdb&quot;,\n      &quot;connection.username&quot;: &quot;banking&quot;,\n      &quot;connection.password&quot;: &quot;banking&quot;,\n      &quot;insert.mode&quot;: &quot;upsert&quot;,\n      &quot;delete.enabled&quot;: &quot;true&quot;,\n      &quot;primary.key.mode&quot;: &quot;record_key&quot;,\n      &quot;schema.evolution&quot;: &quot;basic&quot;,\n      &quot;topics.regex&quot;: &quot;oracle\\\\.BANKING\\\\..+&quot;,\n      &quot;table.name.format&quot;: &quot;banking.${topic}&quot;,\n      &quot;transforms&quot;: &quot;route&quot;,\n      &quot;transforms.route.type&quot;: &quot;org.apache.kafka.connect.transforms.RegexRouter&quot;,\n      &quot;transforms.route.regex&quot;: &quot;oracle\\\\.BANKING\\\\.(.*)&quot;,\n      &quot;transforms.route.replacement&quot;: &quot;$1&quot;\n    }\n  }&#039;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">A los pocos segundos del registro, aparecen las filas de instant\u00e1nea en PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Las 18 filas de BANKING se replican en menos de un minuto.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cualquier cambio posterior en Oracle se propaga a PostgreSQL en segundos.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-7-add-a-new-table-mid-stream\">Paso 7: Agregar una nueva tabla en medio del flujo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed es donde la canalizaci\u00f3n demuestra su val\u00eda.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A\u00f1adir un <code>PR\u00c9STAMOS<\/code> tabla a BANCO mientras la replicaci\u00f3n se est\u00e1 ejecutando.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE TABLE loans (\n  loan_id        NUMBER(10) PRIMARY KEY,\n  customer_id    NUMBER(8) NOT NULL,\n  loan_type      VARCHAR2(30),\n  principal      NUMBER(15,2) NOT NULL,\n  interest_rate  NUMBER(5,2),\n  start_date     DATE,\n  status         VARCHAR2(20),\n  CONSTRAINT fk_loan_customer FOREIGN KEY (customer_id) REFERENCES customers(customer_id)\n);\n\nINSERT INTO loans VALUES (1, 1001, &#039;MORTGAGE&#039;, 250000.00, 3.25, DATE &#039;2023-01-15&#039;, &#039;ACTIVE&#039;);\nINSERT INTO loans VALUES (2, 1002, &#039;AUTO&#039;,     18000.00,  4.50, DATE &#039;2024-06-01&#039;, &#039;ACTIVE&#039;);\nINSERT INTO loans VALUES (3, 1003, &#039;PERSONAL&#039;, 5000.00,   6.75, DATE &#039;2025-02-10&#039;, &#039;ACTIVE&#039;);\nCOMMIT;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">El conector de Oracle capta la nueva tabla porque coincide <code>BANCOS\\..*<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un tema nuevo <code>oracle.BANKING.PRESTAMOS<\/code> se aparece en Kafka.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El sink JDBC capta ese tema porque coincide <code>oracle\\.BANKING\\..+<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Una mesa nueva <code>banca.pr\u00e9stamos<\/code> se crea en PostgreSQL con las tres filas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sin reconfiguraci\u00f3n del conector.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No reiniciar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No redesplegar.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-schema-evolution-tradeoff\">La compensaci\u00f3n del esquema.evoluci\u00f3n<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>schema.evolution: b\u00e1sico<\/code> es lo que hizo la autocreaci\u00f3n de <code>banca.pr\u00e9stamos<\/code> trabajar sin intervenci\u00f3n manual.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tambi\u00e9n es lo que descalifica esa configuraci\u00f3n para la producci\u00f3n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ejecutar <code>bancaria<\/code> en PostgreSQL despu\u00e9s de la replicaci\u00f3n con esta configuraci\u00f3n y la tabla se ve as\u00ed:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n                  Table &quot;banking.accounts&quot;\n    Column    |              Type              | Nullable\n--------------+--------------------------------+----------\n account_id   | bigint                         | not null\n customer_id  | integer                        | not null\n branch_id    | integer                        | not null\n account_type | text                           |\n balance      | numeric(15,2)                  |\n opened_at    | timestamp(6) without time zone |\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Los tipos num\u00e9ricos y de fecha se replican exactamente: <code>entero largo<\/code>, <code>entero<\/code>, <code>numeric(15,2)<\/code>, <code>marca de tiempo(6)<\/code>. Se conservan las restricciones NOT NULL en las columnas num\u00e9ricas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Las columnas de cadena siguen aterrizando como <code>texto<\/code> independientemente de la longitud de origen. El receptor JDBC trata <code>texto<\/code> y <code>varchar<\/code> tan funcionalmente equivalente \u2014 mismo almacenamiento, mismo rendimiento \u2014 para que elija <code>texto<\/code> incondicionalmente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>DONDE customer_id = 1001<\/code> ahora funciona sin comillas. <code>equilibrio<\/code> est\u00e1 correctamente <code>numeric(15,2)<\/code>. La r\u00e9plica es, aproximadamente, del tipo 80% con fidelidad de tipo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El espacio restante \u2014 las restricciones de longitud de VARCHAR \u2014 depende de cu\u00e1l de las tres aproximaciones elijas:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Configuraci\u00f3n<\/th><th>Tipos num\u00e9ricos<\/th><th>Tipos de cadena<\/th><th>Fidelidad<\/th><\/tr><\/thead><tbody><tr><td>No <code>datatype.propagate.source.type<\/code><\/td><td><code>texto<\/code> en todas partes<\/td><td><code>texto<\/code> en todas partes<\/td><td>~0%<\/td><\/tr><tr><td><code>datatype.propagate.source.type<\/code> establecer (este laboratorio)<\/td><td>exacto \u2014 bigint, entero, num\u00e9rico(p,s)<\/td><td><code>texto<\/code>, p\u00e9rdida de longitud<\/td><td>~80%<\/td><\/tr><tr><td>ora2pg esquema + <code>schema.evolution<\/code> discapacitado<\/td><td>exacto<\/td><td>exacto \u2014 VARCHAR(n) conservado<\/td><td>100%<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Para m\u00e1xima fidelidad en producci\u00f3n:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Genera el esquema de PostgreSQL de antemano utilizando ora2pg.<br>El esquema obtiene tipos exactos: <code>NUMERIC(15,2)<\/code> para cantidades, <code>ENTERO<\/code> para identificaciones, apropiado <code>VARCHAR(n)<\/code> longitudes.<\/li>\n\n\n\n<li>Desactivar <code>schema.evolution<\/code> en el conector del fregadero.<br>Debezium transmite datos al esquema existente sin modificarlo.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">La funci\u00f3n de evoluci\u00f3n de esquemas es para laboratorios y prototipos: \u00fatil para demostrar que el proceso funciona, pero insuficiente para producci\u00f3n.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-new-table-discovery-delay\">El retraso en el descubrimiento de la nueva tabla<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Cuando una nueva tabla aparece en Oracle mientras la replicaci\u00f3n est\u00e1 en ejecuci\u00f3n, hay un peque\u00f1o pero real retraso antes de que aparezca en PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El primero <code>SELECCIONE<\/code> en la nueva tabla de PostgreSQL puede devolver <code>la relaci\u00f3n no existe<\/code> aunque el tema de Kafka ya est\u00e9 poblado.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La secuencia es:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Oracle confirma el DDL; la nueva tabla aparece en el registro de rehacer.<\/li>\n\n\n\n<li>Debezium captura el DDL y crea el tema de Kafka.<br>En este punto el tema existe, pero el conector del sink a\u00fan no lo ha visto.<\/li>\n\n\n\n<li>Los INSERTs se publican en el nuevo tema.<\/li>\n\n\n\n<li>El conector del sink eventualmente actualiza sus metadatos de tema.<br>El predeterminado <code>metadata.max.age.ms<\/code> son 5 minutos, aunque Connect normalmente se actualiza con m\u00e1s frecuencia.<br>Cuando lo hace, el \"sink\" descubre el nuevo tema, lee el esquema del primer evento, ejecuta <code>CREAR TABLA<\/code> en PostgreSQL v\u00eda <code>schema.evolution: b\u00e1sico<\/code>, y luego se aplican los eventos.<\/li>\n\n\n\n<li>La tabla ahora existe en PostgreSQL.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Retraso t\u00edpico de descubrimiento de extremo a extremo: 5-30 segundos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En una migraci\u00f3n real, esto importa si se espera que una aplicaci\u00f3n escriba en una tabla completamente nueva de Oracle y la lea inmediatamente desde PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dos maneras de manejarlo:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Bajar <code>consumer.override.metadata.max.age.ms<\/code> en la configuraci\u00f3n del conector de sink (por ejemplo, a 5 segundos) para un descubrimiento m\u00e1s r\u00e1pido.<\/li>\n\n\n\n<li>Pre-crea tablas en PostgreSQL con tipos correctos y desact\u00edvalas <code>schema.evolution<\/code> \u2014 el patr\u00f3n de producci\u00f3n recomendado anteriormente.<br>Las tablas existen antes de que fluya ning\u00fan dato.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"production-gotchas\">Problemas en la producci\u00f3n<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Algunas cosas a tener en cuenta en producci\u00f3n que el laboratorio no revela.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Tablas sin claves primarias.<\/strong><br>Los eventos UPDATE y DELETE de Debezium est\u00e1n indexados por la clave primaria.<br>Las tablas sin una clave primaria (PK) pueden ser \"snapshotizadas\" y las inserciones (INSERTs) replicadas, pero las actualizaciones (UPDATEs) y eliminaciones (DELETEs) no pueden aplicarse a PostgreSQL.<br>Audita el esquema de origen para tablas sin clave primaria antes de definir el alcance de la migraci\u00f3n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Retenci\u00f3n de registros de archivo de LogMiner.<\/strong><br>LogMiner solo puede analizar registros de archivo que a\u00fan existen en el disco.<br>Si su pol\u00edtica de retenci\u00f3n elimina los registros de archivo despu\u00e9s de 24 horas y Debezium se retrasa m\u00e1s que eso, la canalizaci\u00f3n no puede reanudarse desde donde se detuvo.<br>Tama\u00f1o del registro de archivo para la retenci\u00f3n, para el peor escenario de inactividad de Debezium, multiplique su retenci\u00f3n habitual por 3 para mayor seguridad.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Persistencia de contenedores.<\/strong><br>Correr <code>docker run -it<\/code> ata el contenedor a la terminal.<br>Cerrar la terminal detiene el contenedor y rompe la tuber\u00eda.<br>Utilice <code>-d<\/code> (desvinculado) para todo lo que dure m\u00e1s que una prueba manual.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Compensaciones del grupo de consumidores.<\/strong><br>Eliminar un conector de Sink y recrearlo con el mismo nombre no restablece su posici\u00f3n en Kafka.<br>El nuevo conector recoge en el offset confirmado anterior, omitiendo cualquier evento en cola entre medias.<br>Restablecer los desplazamientos expl\u00edcitamente con <code>kafka-consumer-groups.sh --reset-offsets --to-earliest<\/code> antes de volver a registrarse.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Nombre del esquema en may\u00fasculas.<\/strong><br>Los nombres de esquema de Oracle son may\u00fasculas por defecto.<br>En <code>schema.include.list<\/code> el valor en la configuraci\u00f3n del conector debe coincidir exactamente <code>BANCA<\/code>, no <code>banca<\/code>.<br>Los nombres de los temas del lado del fregadero siguen el caso de Oracle, raz\u00f3n por la cual <code>RegexRouter<\/code> elimina un prefijo en may\u00fasculas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"frequently-asked-questions\">Preguntas frecuentes<\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list\">\n<div id=\"faq-question-1777800041851\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfPuede Debezium migrar el esquema de Oracle, o solo los datos?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>Solo los datos.<br \/>Debezium captura eventos DML \u2014 INSERT, UPDATE, DELETE \u2014 del registro de rehacer de Oracle.<br \/>Las definiciones de esquema, los procedimientos almacenados, los desencadenadores, las vistas y las secuencias deben migrarse por separado, normalmente con ora2pg.<br \/>El `schema.evolution: basic feature` puede crear tablas de destino autom\u00e1ticamente, pero con tipos simplificados y sin restricciones: bueno para laboratorios, malo para producci\u00f3n.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1777800042851\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfCu\u00e1l es la latencia entre Oracle y PostgreSQL?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>Unos segundos en estado estable en una configuraci\u00f3n saludable de LogMiner.<br \/>La latencia aumenta bajo una carga intensa de escritura o si la generaci\u00f3n de registros de archivo supera la tasa de lectura de LogMiner.<br \/>Planifique un desfase inferior a un minuto y monitoreelo expl\u00edcitamente con kafka-consumer-groups.sh \u2013describe.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1777800043851\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfEst\u00e1 LogMiner obsoleto en Oracle 19c o 21c?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>LogMiner es el mecanismo CDC admitido en Oracle 19c.<br \/>En Oracle 21c y posteriores, Oracle introdujo XStream como una alternativa de pago.<br \/>Debezium sigue admitiendo ambos: el adaptador LogMiner es el predeterminado y funciona sin una licencia adicional de Oracle.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1777800044851\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfEsto puede ejecutarse contra Oracle Standard Edition?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>S\u00ed, con advertencias.<br \/>LogMiner est\u00e1 disponible en Standard Edition 2, pero se requiere el registro suplementario y el modo archivelog, y algunas optimizaciones disponibles en Enterprise Edition no lo est\u00e1n.<br \/>Prueba contra la edici\u00f3n exacta que tengas.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1777800045851\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfPuedo usar Debezium para migraci\u00f3n de un solo disparo sin mantenerlo en ejecuci\u00f3n?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>S\u00ed.<br \/>Puedes ejecutar la fase de instant\u00e1nea, esperar a que se complete y luego detener el conector.<br \/>Pero rara vez es la decisi\u00f3n correcta; si vas a configurar Debezium, puedes dejar que transmita hasta el cambio para tener un pipeline probado y sin latencia listo cuando la aplicaci\u00f3n se reoriente.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"in-summary\">En resumen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium convierte una migraci\u00f3n de Oracle a PostgreSQL de una simple operaci\u00f3n de volcar y restaurar en un canal controlable y observable que puedes ejecutar durante semanas antes del cambio.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">LogMiner lee los registros de rehacer de Oracle, Kafka mantiene el flujo de eventos y el sink JDBC aplica los cambios a PostgreSQL con una latencia inferior al minuto.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El resultado es un canal de replicaci\u00f3n en tiempo real que puede validar, reproducir y cancelar limpiamente \u2014 la base de cualquier migraci\u00f3n con tiempo de inactividad cero o casi cero.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si est\u00e1 planeando una migraci\u00f3n de Oracle a PostgreSQL y desea ayuda para dise\u00f1ar la fase de corte o revisar su arquitectura CDC, <a href=\"https:\/\/rootfan.com\/es\/servicios\/\">ponerse en contacto<\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>TL;DR: Debezium lee los registros de rehacer (redo logs) de Oracle a trav\u00e9s de LogMiner, publica cada cambio como un evento de Kafka y un conector JDBC de destino aplica esos eventos a PostgreSQL en tiempo real. El resultado es una canalizaci\u00f3n de replicaci\u00f3n transparente y reproducible que puede ejecutar durante semanas antes del corte, \u00fatil para cualquier migraci\u00f3n de Oracle a PostgreSQL que necesite cero o casi cero... <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/rootfan.com\/es\/oracle-to-postgresql-replication-debezium\/\" class=\"more-link\">Seguir leyendo<span class=\"screen-reader-text\"> \u201cC\u00f3mo configurar la replicaci\u00f3n en tiempo real de Oracle a PostgreSQL usando Debezium\u201d<\/span><\/a><\/p>","protected":false},"author":1,"featured_media":6833,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_focus_keyword":"oracle to postgresql replication","rank_math_title":"How to Set Up Real-Time Oracle to PostgreSQL Replication Using Debezium","rank_math_description":"Step-by-step guide to setting up real-time Oracle to PostgreSQL replication using Debezium and Kafka. Covers LogMiner setup, capture user grants, sink connector config, schema.evolution gotchas, and production tips.","rank_math_robots":"","rank_math_og_title":"","rank_math_og_description":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[146],"tags":[143,137,81],"class_list":["post-6832","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oracle-to-postgresql","tag-data-migration","tag-migration","tag-step-by-step"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/rootfan.com\/wp-content\/uploads\/pexels-photo-33593134.jpeg?fit=1880%2C1253&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/posts\/6832","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/comments?post=6832"}],"version-history":[{"count":12,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/posts\/6832\/revisions"}],"predecessor-version":[{"id":6846,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/posts\/6832\/revisions\/6846"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/media\/6833"}],"wp:attachment":[{"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/media?parent=6832"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/categories?post=6832"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/tags?post=6832"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}