Comment configurer la réplication Oracle en temps réel vers PostgreSQL avec Debezium

TL;DR : Debezium lit les journaux de transactions Oracle via LogMiner, publie chaque modification sous forme d'événement Kafka, et un connecteur JDBC sink applique ces événements à PostgreSQL en temps réel.
Le résultat est un pipeline de réplication transparent et rejouable que vous pouvez exécuter pendant des semaines avant la mise en production — utile pour toute migration d'Oracle vers PostgreSQL qui nécessite un temps d'arrêt nul ou quasi nul.
Ce post présente une configuration fonctionnelle de bout en bout, sur de vrais serveurs Oracle et PostgreSQL, avec un schéma bancaire autonome que vous pouvez copier et reproduire.


La plupart des projets de migration d'Oracle vers PostgreSQL échouent lors du basculement, pas lors de la conversion du schéma.

Le travail sur le schéma est difficile mais limité – vous y consacrez quelques semaines, vous corrigez ce que l'outil de conversion ne peut pas traduire, et vous passez à autre chose.

La transition est ouverte.

Vous avez une base de données Oracle de production qui traite des milliers de transactions par seconde.

Vous devez faire passer l'application à PostgreSQL sans perdre une seule ligne, et idéalement sans fenêtre de maintenance de plusieurs heures.

Une migration basée sur des instantanés seule — dump, restore, repoint — ne résout pas ce problème.

Au moment où vous prenez le cliché, chaque nouvel INSERT, UPDATE et DELETE sur Oracle est invisible pour PostgreSQL.

Vous avez besoin d'un outil qui capture les modifications continues d'Oracle et les applique à PostgreSQL en continu.

Debezium est la norme open-source pour cette tâche.

Ce que fait Debezium

Debezium est un framework de capture de données de modification (CDC) construit sur Kafka Connect.

Pour Oracle, il utilise l'adaptateur LogMiner — une fonctionnalité intégrée à Oracle qui lit les journaux de transactions et expose chaque modification validée sous forme de flux structuré.

Debezium interroge LogMiner, convertit chaque modification de ligne en un événement Kafka et publie l'événement sur un sujet Kafka.

Un second connecteur — le Debezium JDBC sink — s'abonne à ces topics et applique les changements à une base de données cible.

Pour la réplication d'Oracle vers PostgreSQL, le pipeline ressemble à ceci :

Oracle (LogMiner) → source Debezium → Kafka → sink JDBC Debezium → PostgreSQL

Les deux connecteurs s'exécutent à l'intérieur d'un seul processus Debezium Connect.

Kafka détient des événements sur des sujets nommés d'après chaque table Oracle.

Vous pouvez arrêter le récepteur et laisser les événements s'accumuler dans Kafka.

Vous pouvez rejouer des événements à partir d'un décalage spécifique.

Vous pouvez exécuter deux flux en parallèle — l'un vers PostgreSQL, l'autre vers un environnement de staging pour les tests.

C'est ce qui rend Debezium utile pour les travaux de migration : vous pouvez voir exactement ce qui se passe à chaque étape, et vous pouvez intervenir à l'une d'entre elles.

L'environnement

Trois VM sur le même réseau.

  • srv1 (192.168.0.180) exécute Oracle 19c Enterprise Edition avec un CDB nommé ORADB et une base de données enfichable pdb1.
  • srvdebezium (192.168.0.230) exécute Ubuntu 24.04 avec Docker, hébergeant le broker Kafka et le conteneur Debezium Connect.
  • srv2 (192.168.0.181) exécute PostgreSQL 18 depuis le dépôt officiel PGDG, hébergeant la base de données cible base de données bancaire.

Le pipeline réplique un petit schéma bancaire d'Oracle vers PostgreSQL : cinq tables (branches, employés, clients, comptes, transactions) avec des relations de clé étrangère, des types de données mixtes et 18 lignes d'échantillon.

Le DDL complet et les données d'exemple sont ci-dessous — reproductibles par copier-coller à partir d'une installation Oracle propre.

Étape 1 — Préparation de la source Oracle

LogMiner a besoin de trois choses activées avant que Debezium puisse les lire.

Mode Archivlog.
Sans cela, Oracle écrase les anciens journaux de transactions une fois qu'ils ne sont plus nécessaires à la récupération d'instance.
LogMiner en a besoin sur le disque pour les lire.
Le passage en mode archivelog nécessite un redémarrage de l'instance.

-- On srv1 as SYSDBA
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;
ALTER PLUGGABLE DATABASE pdb1 OPEN;

Journalisation supplémentaire.
Par défaut, Oracle écrit uniquement les colonnes modifiées dans le journal de transactions (redo log).
Debezium a besoin d'images de lignes complètes pour remplir le avant champ dans les événements de mise à jour.

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

Un utilisateur de capture.
Debezium se connecte en tant qu'utilisateur commun (le C## (le préfixe est obligatoire dans une base de données multi-locataire) avec un accès en lecture au dictionnaire de données et aux vues LogMiner.
Appliquez toutes les subventions — Debezium ne pourra pas démarrer si l'une d'entre elles est manquante.

-- On srv1, as SYSDBA, connected to the CDB root

CREATE USER C##DBZUSER IDENTIFIED BY dbz
  DEFAULT TABLESPACE users
  QUOTA UNLIMITED ON users
  CONTAINER=ALL;

-- Session grants
GRANT CREATE SESSION TO C##DBZUSER CONTAINER=ALL;
GRANT SET CONTAINER  TO C##DBZUSER CONTAINER=ALL;

-- LogMiner privilege — required to start and use LogMiner sessions
GRANT LOGMINING TO C##DBZUSER CONTAINER=ALL;

-- Transaction and dictionary access
GRANT SELECT ANY TRANSACTION TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ANY DICTIONARY  TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT_CATALOG_ROLE    TO C##DBZUSER CONTAINER=ALL;
GRANT EXECUTE_CATALOG_ROLE   TO C##DBZUSER CONTAINER=ALL;

-- Flashback and table access — needed for the initial snapshot
GRANT FLASHBACK ANY TABLE TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ANY TABLE    TO C##DBZUSER CONTAINER=ALL;

-- Object creation — Debezium creates internal tracking objects in its own schema
GRANT CREATE TABLE    TO C##DBZUSER CONTAINER=ALL;
GRANT LOCK ANY TABLE  TO C##DBZUSER CONTAINER=ALL;
GRANT CREATE SEQUENCE TO C##DBZUSER CONTAINER=ALL;

-- LogMiner package execution
GRANT EXECUTE ON DBMS_LOGMNR   TO C##DBZUSER CONTAINER=ALL;
GRANT EXECUTE ON DBMS_LOGMNR_D TO C##DBZUSER CONTAINER=ALL;

-- V$ views — Debezium queries these to track log position, archive status, and transactions
GRANT SELECT ON V_$DATABASE             TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$LOG                  TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$LOG_HISTORY          TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$LOGFILE              TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$ARCHIVED_LOG         TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$ARCHIVE_DEST_STATUS  TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$LOGMNR_LOGS          TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$LOGMNR_CONTENTS      TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$LOGMNR_PARAMETERS    TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$TRANSACTION          TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$MYSTAT               TO C##DBZUSER CONTAINER=ALL;
GRANT SELECT ON V_$STATNAME             TO C##DBZUSER CONTAINER=ALL;

Ne court-circuitez pas cela avec ACCORDER DBA — les autorisations explicites sont audibles, réversibles et correspondent exactement à la documentation de Debezium.

Étape 2 — Créer le schéma BANKING

Le schéma ci-dessous est assez petit pour être collé en 30 secondes et assez varié pour tester le comportement des types de données que Debezium gère correctement et incorrectement.

-- On srv1, connected to pdb1 as a privileged user
CREATE USER banking IDENTIFIED BY banking
  DEFAULT TABLESPACE users
  TEMPORARY TABLESPACE temp;
GRANT CONNECT, RESOURCE, CREATE VIEW TO banking;
ALTER USER banking QUOTA UNLIMITED ON users;

Connecter en tant que banque et créer les cinq tables :

CREATE TABLE branches (
  branch_id    NUMBER(6) PRIMARY KEY,
  branch_name  VARCHAR2(100) NOT NULL,
  city         VARCHAR2(50),
  country      VARCHAR2(50),
  opened_date  DATE
);

CREATE TABLE employees (
  employee_id  NUMBER(6) PRIMARY KEY,
  branch_id    NUMBER(6) NOT NULL,
  full_name    VARCHAR2(100) NOT NULL,
  role         VARCHAR2(50),
  hire_date    DATE,
  CONSTRAINT fk_emp_branch FOREIGN KEY (branch_id) REFERENCES branches(branch_id)
);

CREATE TABLE customers (
  customer_id  NUMBER(8) PRIMARY KEY,
  full_name    VARCHAR2(100) NOT NULL,
  email        VARCHAR2(100),
  country      VARCHAR2(50),
  created_at   TIMESTAMP DEFAULT SYSTIMESTAMP
);

CREATE TABLE accounts (
  account_id    NUMBER(10) PRIMARY KEY,
  customer_id   NUMBER(8) NOT NULL,
  branch_id     NUMBER(6) NOT NULL,
  account_type  VARCHAR2(20),
  balance       NUMBER(15,2) DEFAULT 0,
  opened_at     TIMESTAMP DEFAULT SYSTIMESTAMP,
  CONSTRAINT fk_acc_customer FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
  CONSTRAINT fk_acc_branch   FOREIGN KEY (branch_id)   REFERENCES branches(branch_id)
);

CREATE TABLE transactions (
  txn_id       NUMBER(12) PRIMARY KEY,
  account_id   NUMBER(10) NOT NULL,
  employee_id  NUMBER(6),
  txn_type     VARCHAR2(20),
  amount       NUMBER(15,2) NOT NULL,
  txn_date     TIMESTAMP DEFAULT SYSTIMESTAMP,
  description  VARCHAR2(200),
  CONSTRAINT fk_txn_account  FOREIGN KEY (account_id)  REFERENCES accounts(account_id),
  CONSTRAINT fk_txn_employee FOREIGN KEY (employee_id) REFERENCES employees(employee_id)
);

Insérez un petit jeu de données représentatif — trois succursales, quatre employés, trois clients, quatre comptes, quatre transactions :

-- BRANCHES
INSERT INTO branches VALUES (1, 'Madrid Centro',      'Madrid',    'Spain',    DATE '2010-03-15');
INSERT INTO branches VALUES (2, 'Barcelona Diagonal', 'Barcelona', 'Spain',    DATE '2012-06-01');
INSERT INTO branches VALUES (3, 'Lisbon Avenida',     'Lisbon',    'Portugal', DATE '2015-09-10');

-- EMPLOYEES
INSERT INTO employees VALUES (101, 1, 'Maria Lopez',   'Branch Manager',  DATE '2015-01-15');
INSERT INTO employees VALUES (102, 1, 'Carlos Diaz',   'Teller',          DATE '2018-03-22');
INSERT INTO employees VALUES (103, 2, 'Anna Pereira',  'Branch Manager',  DATE '2016-07-01');
INSERT INTO employees VALUES (104, 3, 'Pedro Santos',  'Account Officer', DATE '2020-11-05');

-- CUSTOMERS
INSERT INTO customers VALUES (1001, 'John Smith',    'john@example.com',   'UK',      SYSTIMESTAMP);
INSERT INTO customers VALUES (1002, 'Hans Mueller',  'hans@example.com',   'Germany', SYSTIMESTAMP);
INSERT INTO customers VALUES (1003, 'Sophie Dubois', 'sophie@example.com', 'France',  SYSTIMESTAMP);

-- ACCOUNTS
INSERT INTO accounts VALUES (10001, 1001, 1, 'CHECKING',  5000.00,  SYSTIMESTAMP);
INSERT INTO accounts VALUES (10002, 1001, 1, 'SAVINGS',   25000.00, SYSTIMESTAMP);
INSERT INTO accounts VALUES (10003, 1002, 2, 'CHECKING',  12000.00, SYSTIMESTAMP);
INSERT INTO accounts VALUES (10004, 1003, 3, 'CHECKING',  8500.00,  SYSTIMESTAMP);

-- TRANSACTIONS
INSERT INTO transactions VALUES (100001, 10001, 102, 'DEPOSIT',    1500.00, SYSTIMESTAMP, 'Salary deposit');
INSERT INTO transactions VALUES (100002, 10001, 102, 'WITHDRAWAL', 200.00,  SYSTIMESTAMP, 'ATM withdrawal');
INSERT INTO transactions VALUES (100003, 10003, 103, 'DEPOSIT',    3000.00, SYSTIMESTAMP, 'Cash deposit');
INSERT INTO transactions VALUES (100004, 10004, 104, 'TRANSFER',   500.00,  SYSTIMESTAMP, 'Transfer to savings');

COMMIT;

Dix-huit lignes au total, toutes les clés étrangères satisfaites.

Ceci est l'état source que Debezium prendra en instantané.

Étape 3 — Démarrer Kafka et Kafka Connect

On srvdebezium, créez un réseau Docker et démarrez Kafka en mode détaché pour que le broker survive à la fermeture du terminal.

docker network create debezium-net

docker run -d --name kafka --hostname kafka --network debezium-net \
  -p 9092:9092 \
  -e CLUSTER_ID=$(docker run --rm quay.io/debezium/kafka:3.5 /kafka/bin/kafka-storage.sh random-uuid) \
  -e NODE_ID=1 -e NODE_ROLE=combined \
  -e KAFKA_CONTROLLER_QUORUM_VOTERS=1@kafka:9093 \
  -e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093 \
  -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 \
  quay.io/debezium/kafka:3.5

Le connecteur Debezium pour Oracle est livré sans le pilote JDBC d'Oracle pour des raisons de licence.

Télécharger ojdbc11.jar depuis Maven Central et le monter dans Connect au démarrage :

mkdir -p /home/fernando/oracle-jdbc
curl -L \
  https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc11/21.9.0.0/ojdbc11-21.9.0.0.jar \
  -o /home/fernando/oracle-jdbc/ojdbc11.jar

docker run -d --name connect --network debezium-net \
  -p 8083:8083 \
  -e BOOTSTRAP_SERVERS=kafka:9092 \
  -e GROUP_ID=1 \
  -e CONFIG_STORAGE_TOPIC=my_connect_configs \
  -e OFFSET_STORAGE_TOPIC=my_connect_offsets \
  -e STATUS_STORAGE_TOPIC=my_connect_statuses \
  -v /home/fernando/oracle-jdbc/ojdbc11.jar:/kafka/connect/debezium-connector-oracle/ojdbc11.jar \
  quay.io/debezium/connect:3.5

Utilisation -d (détaché).
Sans cela, fermer le terminal arrête le conteneur — et arrête votre pipeline de réplication.

Si vous évaluez une migration d'Oracle vers PostgreSQL et souhaitez un deuxième avis sur la conception de basculement, J'offre une évaluation de migration à prix fixe →

Étape 4 — Enregistrer le connecteur source Oracle

La configuration du connecteur indique à Debezium quelle base de données lire, quel schéma capturer et où stocker son historique interne des schémas.

curl -i -X POST -H "Content-Type:application/json" \
  http://localhost:8083/connectors/ -d '{
    "name": "oracle-connector",
    "config": {
      "connector.class": "io.debezium.connector.oracle.OracleConnector",
      "tasks.max": "1",
      "database.hostname": "192.168.0.180",
      "database.port": "1521",
      "database.user": "C##DBZUSER",
      "database.password": "dbz",
      "database.dbname": "ORADB",
      "database.pdb.name": "pdb1",
      "topic.prefix": "oracle",
      "schema.include.list": "BANKING",
      "schema.history.internal.kafka.bootstrap.servers": "kafka:9092",
      "schema.history.internal.kafka.topic": "schemahistory.oracle",
      "decimal.handling.mode": "precise",
      "datatype.propagate.source.type": "VARCHAR2,NUMBER,CHAR,NCHAR,NVARCHAR2"
    }
  }'

Quatre paramètres à comprendre :

  • schema.include.list: "BANQUE" capture toutes les tables du schéma BANKING.
    Non tableau.inclure.liste est configuré, de sorte que lorsqu'une nouvelle table apparaît dans BANKING, Debezium la détecte automatiquement — vous verrez cela à l'étape 7.
  • Sujet.préfixe : " oracle " contrôle la nomenclature des sujets Kafka.
    Événements pour BANQUE.TRANSACTIONS atterrir sur oracle.BANQUE.TRANSACTIONS.
    Le nom de la PDB n'apparaît pas dans le nom du sujet, même si database.pdb.nom est défini — il est utilisé uniquement pour le filtrage.
  • mode.de.gestion.decimal: "précis" fait Oracle NOMBRE les valeurs arrivent en Java BigDecimal dans les événements Kafka, en préservant la précision et l'échelle. Le connecteur JDBC l'utilise pour créer numérique(p,s), grand entieret entier colonnes dans PostgreSQL.
  • datatype.propagate.source.type: "VARCHAR2,NUMBER,CHAR,NCHAR,NVARCHAR2" intègre la déclaration de colonne Oracle d'origine dans le schéma de chaque événement en tant que métadonnées. Sans cela, le collecteur ne voit que le type JDBC — CHAÎNE pour chaque colonne — et crée texte pour tout, y compris les colonnes numériques.

Le connecteur démarre immédiatement une phase d'instantané : il analyse toutes les tables capturées, publie chaque ligne existante sous forme d'événement avec un type d'opération r (lire), puis passe en mode diffusion en continu où il suit le journal des redo.

Vous pouvez suivre les progrès avec docker logs -f connect.

Étape 5 — Regarder les événements du CDC en direct

Ouvrir un deuxième terminal sur srvdebezium et démarrer un consommateur de console Kafka pour observer les événements à leur arrivée.

Transférer grep extraire uniquement les champs significatifs — l'enveloppe JSON complète est dominée par la définition du schéma, ce qui est bien pour le traitement mais illisible à l'écran.

docker run -it --rm --name watcher --network debezium-net \
  --entrypoint /kafka/bin/kafka-console-consumer.sh \
  quay.io/debezium/kafka:3.5 \
  --bootstrap-server kafka:9092 \
  --topic oracle.BANKING.TRANSACTIONS \
  --from-beginning 2>/dev/null \
  | grep -oE '"(op|TXN_ID|TXN_TYPE|AMOUNT|DESCRIPTION)":"[^"]*"'

Quatre événements instantanés apparaissent immédiatement, un par transaction existante, chacun avec "op":" lectura".

Maintenant, générez une modification en direct sur Oracle :

INSERT INTO transactions VALUES (100005, 10002, 102, 'DEPOSIT', 5000.00, SYSTIMESTAMP, 'Bonus deposit');
COMMIT;

Un nouvel événement apparaît avec "op":"c" et la rangée complète dans après.

Une mise à jour montre "op":"u" avec les deux avant et après peuplé.

Une suppression montre "op":"d" avec avant peuplé et après nul.

Latence inférieure à la seconde en régime stable.

Étape 6 — Configurer le Sink PostgreSQL

On srv2, créez l'utilisateur cible, la base de données et le schéma, et autorisez les connexions à distance depuis srvdebezium.

sudo -u postgres psql -c "CREATE USER banking WITH PASSWORD 'banking';"
sudo -u postgres psql -c "CREATE DATABASE bankingdb OWNER banking;"
sudo -u postgres psql -d bankingdb -c "CREATE SCHEMA banking AUTHORIZATION banking;"

Modifier les deux fichiers de configuration PostgreSQL sur srv2. listen_addresses = '*' indique à PostgreSQL d'accepter les connexions sur toutes les interfaces réseau, pas seulement sur localhost. Le pg_hba.conf ligne autorise tout utilisateur et base de données à partir du 192.168.0.0/24 sous-réseau pour s'authentifier avec SCRAM (le protocole de hachage sécurisé des mots de passe), qui est la manière dont le connecteur sink JDBC de Debezium accédera à la base de données.

# /etc/postgresql/18/main/postgresql.conf
listen_addresses = '*'

# /etc/postgresql/18/main/pg_hba.conf
host    all    all    192.168.0.0/24    scram-sha-256
sudo systemctl restart postgresql

On srvdebezium, enregistrez le connecteur sink JDBC.

Le RegexRouter Les bandes SMT enlèvent oracle.BANQUE. préfixe de chaque sujet avant qu'il ne devienne le nom de la table cible, donc oracle.BANQUE.TRANSACTIONS cartes vers transactions bancaires plutôt que banque.oracle_banque_transactions.

curl -i -X POST -H "Content-Type:application/json" \
  http://localhost:8083/connectors/ -d '{
    "name": "jdbc-sink-connector",
    "config": {
      "connector.class": "io.debezium.connector.jdbc.JdbcSinkConnector",
      "tasks.max": "1",
      "connection.url": "jdbc:postgresql://192.168.0.181:5432/bankingdb",
      "connection.username": "banking",
      "connection.password": "banking",
      "insert.mode": "upsert",
      "delete.enabled": "true",
      "primary.key.mode": "record_key",
      "schema.evolution": "basic",
      "topics.regex": "oracle\\.BANKING\\..+",
      "table.name.format": "banking.${topic}",
      "transforms": "route",
      "transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter",
      "transforms.route.regex": "oracle\\.BANKING\\.(.*)",
      "transforms.route.replacement": "$1"
    }
  }'

Quelques secondes après l'enregistrement, les rangées d'instantanés apparaissent dans PostgreSQL.

Les 18 lignes de BANKING se répliquent en moins d'une minute.

Toute modification ultérieure sur Oracle se propage à PostgreSQL en quelques secondes.

Étape 7 — Ajouter une nouvelle table en cours de route

C'est là que le pipeline fait ses preuves.

Ajouter un PRÊTS table vers BANQUE pendant que la réplication est en cours.

CREATE TABLE loans (
  loan_id        NUMBER(10) PRIMARY KEY,
  customer_id    NUMBER(8) NOT NULL,
  loan_type      VARCHAR2(30),
  principal      NUMBER(15,2) NOT NULL,
  interest_rate  NUMBER(5,2),
  start_date     DATE,
  status         VARCHAR2(20),
  CONSTRAINT fk_loan_customer FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

INSERT INTO loans VALUES (1, 1001, 'MORTGAGE', 250000.00, 3.25, DATE '2023-01-15', 'ACTIVE');
INSERT INTO loans VALUES (2, 1002, 'AUTO',     18000.00,  4.50, DATE '2024-06-01', 'ACTIVE');
INSERT INTO loans VALUES (3, 1003, 'PERSONAL', 5000.00,   6.75, DATE '2025-02-10', 'ACTIVE');
COMMIT;

Le connecteur Oracle récupère la nouvelle table car elle correspond BANQUE.

Un nouveau sujet oracle.BANQUE.PRETS apparaît dans Kafka.

Le collecteur JDBC détecte ce topic car il correspond oracle\.BANKING\..+.

Une nouvelle table banque.prêts est créé dans PostgreSQL avec les trois lignes.

Aucune reconfiguration du connecteur.

Pas de redémarrage.

Pas de redéploiement.

Le compromis de évolution du schéma

schema.evolution: basique est ce qui a rendu la création automatique de banque.prêts fonctionne sans intervention manuelle.

C'est aussi ce qui disqualifie cette configuration pour la production.

Exécuter comptes bancaires sur PostgreSQL après réplication avec cette configuration et le tableau ressemble à ceci :

                  Table "banking.accounts"
    Column    |              Type              | Nullable
--------------+--------------------------------+----------
 account_id   | bigint                         | not null
 customer_id  | integer                        | not null
 branch_id    | integer                        | not null
 account_type | text                           |
 balance      | numeric(15,2)                  |
 opened_at    | timestamp(6) without time zone |

Les types numériques et de date se reproduisent exactement : grand entier, entier, numérique(15,2), horodatage(6). Les contraintes NOT NULL sur les colonnes numériques sont conservées.

Les colonnes de chaînes de caractères atterrissent toujours en tant que texte quelle que soit la longueur de la source. Le récepteur JDBC traite texte et varchar aussi fonctionnellement équivalentes — même stockage, même performance — donc il choisit texte inconditionnellement.

OÙ customer_id = 1001 fonctionne maintenant sans guillemets. équilibre correctement numérique(15,2). La reproduction est fidèle au type 80%, à peu près.

L'écart restant — les contraintes de longueur VARCHAR — dépend de l'approche que vous choisissez parmi les trois suivantes :

ConfigurationTypes numériquesTypes de chaînesFidélité
Non datatype.propagate.source.typetexte partouttexte partout~0%
datatype.propagate.source.type définir (ce laboratoire)exact — bigint, entier, numérique(p,s)texte, longueur perdue~80%
ora2pg schéma + schéma.évolution désactivéexactexact — VARCHAR(n) conservé100%

Pour une fidélité complète en production :

  1. Construisez le schéma PostgreSQL à l'avance en utilisant ora2pg.
    Le schéma obtient des types exacts : NUMERIC(15,2) pour les montants, ENTIER pour les identifiants, approprié VARCHAR(n) longueurs.
  2. Désactiver schéma.évolution dans le raccord d'évier.
    Debezium diffuse les données dans le schéma existant sans le modifier.

La fonctionnalité d'évolution de schéma est destinée aux laboratoires et aux prototypes — utile pour prouver que le pipeline fonctionne, insuffisante pour la production.

Le délai de découverte de nouvelle table

Lorsqu'une nouvelle table apparaît dans Oracle pendant que la réplication est en cours, il y a un léger mais réel délai avant qu'elle n'apparaisse dans PostgreSQL.

Le premier SÉLECTIONNER sur la nouvelle table PostgreSQL peut retourner la relation n'existe pas même si le topic Kafka est déjà rempli.

La séquence est :

  1. Oracle valide le DDL — la nouvelle table apparaît dans le journal de transactions.
  2. Debezium capture la DDL et crée le topic Kafka.
    À ce stade, le sujet existe, mais le connecteur d'extraction ne l'a pas encore vu.
  3. Les INSERTs publient sur le nouveau sujet.
  4. Le connecteur d'évier rafraîchit éventuellement ses métadonnées de rubrique.
    Le défaut metadata.max.age.ms prend 5 minutes, bien que Connect se rafraîchisse généralement plus souvent.
    Lorsqu'il le fait, le puits découvre le nouveau sujet, lit le schéma du premier événement, puis exécute CRÉER UNE TABLE sur PostgreSQL via schema.evolution: basique, puis applique les événements.
  5. La table existe maintenant sur PostgreSQL.

Délai de découverte de bout en bout typique : 5 à 30 secondes.

Dans une migration réelle, cela importe si une application est censée écrire dans une toute nouvelle table Oracle et la lire immédiatement depuis PostgreSQL.

Deux façons d'y faire face :

  • Plus bas consumer.override.metadata.max.age.ms pour une découverte plus rapide.
  • Pré-créer les tables dans PostgreSQL avec les types corrects et désactiver schéma.évolution — le modèle de production recommandé ci-dessus.
    Les tables existent avant que les données ne circulent.

Problèmes de production

Quelques points à surveiller en production que le laboratoire ne révèle pas.

Tables sans clés primaires.
Les événements UPDATE et DELETE de Debezium sont indexés par la clé primaire.
Les tables sans clé primaire peuvent être instantanées et les insertions répliquées, mais les mises à jour et les suppressions ne peuvent pas être appliquées à PostgreSQL.
Auditez le schéma source pour les tables sans clé primaire avant de cadrer la migration.

Rétention des journaux d'archive LogMiner.
LogMiner ne peut analyser que les journaux d'archives qui existent toujours sur le disque.
Si votre politique de rétention supprime les journaux d'archives après 24 heures et que Debezium prend du retard de plus que cela, le pipeline ne peut pas reprendre là où il s'est arrêté.
Dimensionnez la rétention des journaux d'archives à votre pire scénario de temps d'arrêt Debezium — multipliez votre rétention habituelle par 3 pour des raisons de sécurité.

Persistance de conteneur.
Course à pied docker run -it attache le conteneur au terminal.
Fermer le terminal arrête le conteneur et interrompt le pipeline.
Utilisation -d (détaché) pour tout ce qui dure plus longtemps qu'un test manuel.

Crédits de groupe de consommateurs.
La suppression d'un connecteur Sink et sa recréation sous le même nom ne réinitialise pas sa position dans Kafka.
Le nouveau connecteur reprend à l'offset précédemment validé, en ignorant tous les événements mis en file d'attente entre-temps.
Réinitialiser explicitement les décalages avec kafka-consumer-groups.sh --reset-offsets --to-earliest avant de vous réinscrire.

Nom du schéma en casse.
Les noms de schémas Oracle sont en majuscules par défaut.
Le schema.include.liste la valeur dans la configuration du connecteur doit correspondre exactement — LA BANQUE, pas banque.
Les noms de sujets de type "sink-side" suivent le format Oracle, c'est pourquoi RegexRouter enlève un préfixe en majuscules.

Foire aux questions

Debezium peut-il migrer le schéma Oracle, ou seulement les données ?

Seulement les données.
Debezium capture les événements DML — INSERT, UPDATE, DELETE — du journal de redo d'Oracle.
Les définitions de schémas, les procédures stockées, les déclencheurs, les vues et les séquences doivent être migrés séparément, généralement avec ora2pg.
Le schéma.évolution : la fonctionnalité de base peut auto-créer les tables cibles mais avec des types simplifiés et sans contraintes — tant mieux pour les laboratoires, mais pas pour la production.

Quelle est la latence entre Oracle et PostgreSQL ?

Quelques secondes en régime permanent sur une configuration LogMiner saine.
La latence augmente sous une forte charge d'écriture ou si la génération des journaux d'archivage dépasse le taux de lecture de LogMiner.
Planifiez un décalage inférieur à une minute et surveillez-le explicitement avec kafka-consumer-groups.sh –describe.

LogMiner est-il obsolète dans Oracle 19c ou 21c ?

LogMiner est le mécanisme CDC pris en charge dans Oracle 19c.
Dans Oracle 21c et versions ultérieures, Oracle a introduit XStream comme alternative payante.
Debezium continue de prendre en charge les deux : l'adaptateur LogMiner est celui par défaut et fonctionne sans licence Oracle supplémentaire.

Est-ce que cela peut s'exécuter contre Oracle Standard Edition ?

Oui, avec des réserves.
LogMiner est disponible en Standard Edition 2, mais le journalisation supplémentaire et le mode archivelog sont requis, et certaines optimisations disponibles en Enterprise Edition ne le sont pas.
Testez par rapport à l'édition exacte que vous possédez.

Puis-je utiliser Debezium pour une migration ponctuelle sans le maintenir en fonctionnement ?

Oui.
Vous pouvez exécuter la phase d'instantané, attendre qu'elle se termine, puis arrêter le connecteur.
Mais c'est rarement la bonne décision — si vous allez configurer Debezium, autant le laisser diffuser jusqu'au basculement afin d'avoir un pipeline testé et sans latence prêt lorsque l'application sera redirigée.

En résumé

Debezium transforme une migration d'Oracle vers PostgreSQL d'une simple opération de vidage et restauration en un pipeline contrôlable et observable que vous pouvez exécuter pendant des semaines avant la bascule.

LogMiner lit le redo log d'Oracle, Kafka détient le flux d'événements et le sink JDBC applique les modifications à PostgreSQL avec une latence inférieure à la minute.

Le résultat est un canal de réplication en temps réel que vous pouvez valider, rejouer et annuler proprement — la base de toute migration avec interruption nulle ou quasi nulle.

Si vous prévoyez une migration d'Oracle vers PostgreSQL et souhaitez de l'aide pour concevoir la phase de basculement ou réviser votre architecture CDC, prendre contact →

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *