{"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":"replication-doracle-vers-postgresql-avec-debezium","status":"publish","type":"post","link":"https:\/\/rootfan.com\/fr\/oracle-to-postgresql-replication-debezium\/","title":{"rendered":"Comment configurer la r\u00e9plication Oracle en temps r\u00e9el vers PostgreSQL avec Debezium"},"content":{"rendered":"<p class=\"wp-block-paragraph\" id=\"tl-dr\"><strong>TL;DR :<\/strong> Debezium lit les journaux de transactions Oracle via LogMiner, publie chaque modification sous forme d'\u00e9v\u00e9nement Kafka, et un connecteur JDBC sink applique ces \u00e9v\u00e9nements \u00e0 PostgreSQL en temps r\u00e9el.<br>Le r\u00e9sultat est un pipeline de r\u00e9plication transparent et rejouable que vous pouvez ex\u00e9cuter pendant des semaines avant la mise en production \u2014 utile pour toute migration d'Oracle vers PostgreSQL qui n\u00e9cessite un temps d'arr\u00eat nul ou quasi nul.<br>Ce post pr\u00e9sente une configuration fonctionnelle de bout en bout, sur de vrais serveurs Oracle et PostgreSQL, avec un sch\u00e9ma bancaire autonome que vous pouvez copier et reproduire.<\/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 plupart des projets de migration d'Oracle vers PostgreSQL \u00e9chouent lors du basculement, pas lors de la conversion du sch\u00e9ma.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le travail sur le sch\u00e9ma est difficile mais limit\u00e9 \u2013 vous y consacrez quelques semaines, vous corrigez ce que l'outil de conversion ne peut pas traduire, et vous passez \u00e0 autre chose.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La transition est ouverte.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vous avez une base de donn\u00e9es Oracle de production qui traite des milliers de transactions par seconde.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vous devez faire passer l'application \u00e0 PostgreSQL sans perdre une seule ligne, et id\u00e9alement sans fen\u00eatre de maintenance de plusieurs heures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une migration bas\u00e9e sur des instantan\u00e9s seule \u2014 dump, restore, repoint \u2014 ne r\u00e9sout pas ce probl\u00e8me.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Au moment o\u00f9 vous prenez le clich\u00e9, chaque nouvel INSERT, UPDATE et DELETE sur Oracle est invisible pour PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vous avez besoin d'un outil qui capture les modifications continues d'Oracle et les applique \u00e0 PostgreSQL en continu.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium est la norme open-source pour cette t\u00e2che.<\/p>\n\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Table des mati\u00e8res<\/h2><nav><ul><li><a href=\"#what-debezium-does\">Ce que fait Debezium<\/a><\/li><li><a href=\"#the-lab-environment\">L'environnement<\/a><\/li><li><a href=\"#step-1-prepare-the-oracle-source\">\u00c9tape 1 \u2014 Pr\u00e9paration de la source Oracle<\/a><\/li><li><a href=\"#step-2-create-the-banking-schema\">\u00c9tape 2 \u2014 Cr\u00e9er le sch\u00e9ma BANKING<\/a><\/li><li><a href=\"#step-3-start-kafka-and-kafka-connect\">\u00c9tape 3 \u2014 D\u00e9marrer Kafka et Kafka Connect<\/a><\/li><li><a href=\"#step-4-register-the-oracle-source-connector\">\u00c9tape 4 \u2014 Enregistrer le connecteur source Oracle<\/a><\/li><li><a href=\"#step-5-watch-cdc-events-live\">\u00c9tape 5 \u2014 Regarder les \u00e9v\u00e9nements du CDC en direct<\/a><\/li><li><a href=\"#step-6-set-up-the-postgre-sql-sink\">\u00c9tape 6 \u2014 Configurer le Sink PostgreSQL<\/a><\/li><li><a href=\"#step-7-add-a-new-table-mid-stream\">\u00c9tape 7 \u2014 Ajouter une nouvelle table en cours de route<\/a><\/li><li><a href=\"#the-schema-evolution-tradeoff\">Le compromis de \u00e9volution du sch\u00e9ma<\/a><\/li><li><a href=\"#the-new-table-discovery-delay\">Le d\u00e9lai de d\u00e9couverte de nouvelle table<\/a><\/li><li><a href=\"#production-gotchas\">Probl\u00e8mes de production<\/a><\/li><li><a href=\"#frequently-asked-questions\">Foire aux questions<\/a><ul><li><a href=\"#faq-question-1777800041851\">Debezium peut-il migrer le sch\u00e9ma Oracle, ou seulement les donn\u00e9es ?<\/a><\/li><li><a href=\"#faq-question-1777800042851\">Quelle est la latence entre Oracle et PostgreSQL ?<\/a><\/li><li><a href=\"#faq-question-1777800043851\">LogMiner est-il obsol\u00e8te dans Oracle 19c ou 21c ?<\/a><\/li><li><a href=\"#faq-question-1777800044851\">Est-ce que cela peut s'ex\u00e9cuter contre Oracle Standard Edition ?<\/a><\/li><li><a href=\"#faq-question-1777800045851\">Puis-je utiliser Debezium pour une migration ponctuelle sans le maintenir en fonctionnement ?<\/a><\/li><\/ul><\/li><li><a href=\"#in-summary\">En r\u00e9sum\u00e9<\/a><\/li><\/ul><\/nav><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-debezium-does\">Ce que fait Debezium<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium est un framework de capture de donn\u00e9es de modification (CDC) construit sur Kafka Connect.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour Oracle, il utilise l'adaptateur LogMiner \u2014 une fonctionnalit\u00e9 int\u00e9gr\u00e9e \u00e0 Oracle qui lit les journaux de transactions et expose chaque modification valid\u00e9e sous forme de flux structur\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium interroge LogMiner, convertit chaque modification de ligne en un \u00e9v\u00e9nement Kafka et publie l'\u00e9v\u00e9nement sur un sujet Kafka.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un second connecteur \u2014 le Debezium JDBC sink \u2014 s'abonne \u00e0 ces topics et applique les changements \u00e0 une base de donn\u00e9es cible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour la r\u00e9plication d'Oracle vers PostgreSQL, le pipeline ressemble \u00e0 ceci :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Oracle (LogMiner) \u2192 source Debezium \u2192 Kafka \u2192 sink JDBC Debezium \u2192 PostgreSQL<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Les deux connecteurs s'ex\u00e9cutent \u00e0 l'int\u00e9rieur d'un seul processus Debezium Connect.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Kafka d\u00e9tient des \u00e9v\u00e9nements sur des sujets nomm\u00e9s d'apr\u00e8s chaque table Oracle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vous pouvez arr\u00eater le r\u00e9cepteur et laisser les \u00e9v\u00e9nements s'accumuler dans Kafka.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vous pouvez rejouer des \u00e9v\u00e9nements \u00e0 partir d'un d\u00e9calage sp\u00e9cifique.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vous pouvez ex\u00e9cuter deux flux en parall\u00e8le \u2014 l'un vers PostgreSQL, l'autre vers un environnement de staging pour les tests.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C'est ce qui rend Debezium utile pour les travaux de migration : vous pouvez voir exactement ce qui se passe \u00e0 chaque \u00e9tape, et vous pouvez intervenir \u00e0 l'une d'entre elles.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-lab-environment\">L'environnement<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Trois VM sur le m\u00eame r\u00e9seau.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>srv1<\/code> (192.168.0.180) ex\u00e9cute Oracle 19c Enterprise Edition avec un CDB nomm\u00e9 <code>ORADB<\/code> et une base de donn\u00e9es enfichable <code>pdb1<\/code>.<\/li>\n\n\n\n<li><code>srvdebezium<\/code> (192.168.0.230) ex\u00e9cute Ubuntu 24.04 avec Docker, h\u00e9bergeant le broker Kafka et le conteneur Debezium Connect.<\/li>\n\n\n\n<li><code>srv2<\/code> (192.168.0.181) ex\u00e9cute PostgreSQL 18 depuis le d\u00e9p\u00f4t officiel PGDG, h\u00e9bergeant la base de donn\u00e9es cible <code>base de donn\u00e9es bancaire<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Le pipeline r\u00e9plique un petit sch\u00e9ma bancaire d'Oracle vers PostgreSQL : cinq tables (<code>branches<\/code>, <code>employ\u00e9s<\/code>, <code>clients<\/code>, <code>comptes<\/code>, <code>transactions<\/code>) avec des relations de cl\u00e9 \u00e9trang\u00e8re, des types de donn\u00e9es mixtes et 18 lignes d'\u00e9chantillon.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le DDL complet et les donn\u00e9es d'exemple sont ci-dessous \u2014 reproductibles par copier-coller \u00e0 partir d'une installation Oracle propre.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-1-prepare-the-oracle-source\">\u00c9tape 1 \u2014 Pr\u00e9paration de la source Oracle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">LogMiner a besoin de trois choses activ\u00e9es avant que Debezium puisse les lire.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Mode Archivlog.<\/strong><br>Sans cela, Oracle \u00e9crase les anciens journaux de transactions une fois qu'ils ne sont plus n\u00e9cessaires \u00e0 la r\u00e9cup\u00e9ration d'instance.<br>LogMiner en a besoin sur le disque pour les lire.<br>Le passage en mode archivelog n\u00e9cessite un red\u00e9marrage de l'instance.<\/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>Journalisation suppl\u00e9mentaire.<\/strong><br>Par d\u00e9faut, Oracle \u00e9crit uniquement les colonnes modifi\u00e9es dans le journal de transactions (redo log).<br>Debezium a besoin d'images de lignes compl\u00e8tes pour remplir le <code>avant<\/code> champ dans les \u00e9v\u00e9nements de mise \u00e0 jour.<\/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>Un utilisateur de capture.<\/strong><br>Debezium se connecte en tant qu'utilisateur commun (le <code>C##<\/code> (le pr\u00e9fixe est obligatoire dans une base de donn\u00e9es multi-locataire) avec un acc\u00e8s en lecture au dictionnaire de donn\u00e9es et aux vues LogMiner.<br>Appliquez toutes les subventions \u2014 Debezium ne pourra pas d\u00e9marrer si l'une d'entre elles est manquante.<\/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\">Ne court-circuitez pas cela avec <code>ACCORDER DBA<\/code> \u2014 les autorisations explicites sont audibles, r\u00e9versibles et correspondent exactement \u00e0 la documentation de Debezium.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-2-create-the-banking-schema\">\u00c9tape 2 \u2014 Cr\u00e9er le sch\u00e9ma BANKING<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Le sch\u00e9ma ci-dessous est assez petit pour \u00eatre coll\u00e9 en 30 secondes et assez vari\u00e9 pour tester le comportement des types de donn\u00e9es que Debezium g\u00e8re correctement et incorrectement.<\/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\">Connecter en tant que <code>banque<\/code> et cr\u00e9er les cinq tables :<\/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\">Ins\u00e9rez un petit jeu de donn\u00e9es repr\u00e9sentatif \u2014 trois succursales, quatre employ\u00e9s, trois clients, quatre comptes, quatre transactions :<\/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\">Dix-huit lignes au total, toutes les cl\u00e9s \u00e9trang\u00e8res satisfaites.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ceci est l'\u00e9tat source que Debezium prendra en instantan\u00e9.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-3-start-kafka-and-kafka-connect\">\u00c9tape 3 \u2014 D\u00e9marrer Kafka et Kafka Connect<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On <code>srvdebezium<\/code>, cr\u00e9ez un r\u00e9seau Docker et d\u00e9marrez Kafka en mode d\u00e9tach\u00e9 pour que le broker survive \u00e0 la fermeture du 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\">Le connecteur Debezium pour Oracle est livr\u00e9 sans le pilote JDBC d'Oracle pour des raisons de licence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">T\u00e9l\u00e9charger <code>ojdbc11.jar<\/code> depuis Maven Central et le monter dans Connect au d\u00e9marrage\u00a0:<\/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\">Utilisation <code>-d<\/code> (d\u00e9tach\u00e9).<br>Sans cela, fermer le terminal arr\u00eate le conteneur \u2014 et arr\u00eate votre pipeline de r\u00e9plication.<\/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 vous \u00e9valuez une migration d'Oracle vers PostgreSQL et souhaitez un deuxi\u00e8me avis sur la conception de basculement, <a href=\"https:\/\/rootfan.com\/fr\/services\/\">J'offre une \u00e9valuation de migration \u00e0 prix fixe \u2192<\/a><\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-4-register-the-oracle-source-connector\">\u00c9tape 4 \u2014 Enregistrer le connecteur source Oracle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La configuration du connecteur indique \u00e0 Debezium quelle base de donn\u00e9es lire, quel sch\u00e9ma capturer et o\u00f9 stocker son historique interne des sch\u00e9mas.<\/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\">Quatre param\u00e8tres \u00e0 comprendre :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>schema.include.list: \"BANQUE\"<\/code> capture toutes les tables du sch\u00e9ma BANKING.<br>Non <code>tableau.inclure.liste<\/code> est configur\u00e9, de sorte que lorsqu'une nouvelle table appara\u00eet dans BANKING, Debezium la d\u00e9tecte automatiquement \u2014 vous verrez cela \u00e0 l'\u00e9tape 7.<\/li>\n\n\n\n<li><code>Sujet.pr\u00e9fixe : \" oracle \"<\/code> contr\u00f4le la nomenclature des sujets Kafka.<br>\u00c9v\u00e9nements pour <code>BANQUE.TRANSACTIONS<\/code> atterrir sur <code>oracle.BANQUE.TRANSACTIONS<\/code>.<br>Le nom de la PDB n'appara\u00eet pas dans le nom du sujet, m\u00eame si <code>database.pdb.nom<\/code> est d\u00e9fini \u2014 il est utilis\u00e9 uniquement pour le filtrage.<\/li>\n\n\n\n<li><code>mode.de.gestion.decimal: \"pr\u00e9cis\"<\/code> fait Oracle <code>NOMBRE<\/code> les valeurs arrivent en Java <code>BigDecimal<\/code> dans les \u00e9v\u00e9nements Kafka, en pr\u00e9servant la pr\u00e9cision et l'\u00e9chelle. Le connecteur JDBC l'utilise pour cr\u00e9er <code>num\u00e9rique(p,s)<\/code>, <code>grand entier<\/code>et <code>entier<\/code> colonnes dans PostgreSQL.<\/li>\n\n\n\n<li><code>datatype.propagate.source.type: \"VARCHAR2,NUMBER,CHAR,NCHAR,NVARCHAR2\"<\/code> int\u00e8gre la d\u00e9claration de colonne Oracle d'origine dans le sch\u00e9ma de chaque \u00e9v\u00e9nement en tant que m\u00e9tadonn\u00e9es. Sans cela, le collecteur ne voit que le type JDBC \u2014 <code>CHA\u00ceNE<\/code> pour chaque colonne \u2014 et cr\u00e9e <code>texte<\/code> pour tout, y compris les colonnes num\u00e9riques.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Le connecteur d\u00e9marre imm\u00e9diatement une phase d'instantan\u00e9 : il analyse toutes les tables captur\u00e9es, publie chaque ligne existante sous forme d'\u00e9v\u00e9nement avec un type d'op\u00e9ration <code>r<\/code> (lire), puis passe en mode diffusion en continu o\u00f9 il suit le journal des redo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vous pouvez suivre les progr\u00e8s avec <code>docker logs -f connect<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-5-watch-cdc-events-live\">\u00c9tape 5 \u2014 Regarder les \u00e9v\u00e9nements du CDC en direct<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ouvrir un deuxi\u00e8me terminal sur <code>srvdebezium<\/code> et d\u00e9marrer un consommateur de console Kafka pour observer les \u00e9v\u00e9nements \u00e0 leur arriv\u00e9e.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Transf\u00e9rer <code>grep<\/code> extraire uniquement les champs significatifs \u2014 l'enveloppe JSON compl\u00e8te est domin\u00e9e par la d\u00e9finition du sch\u00e9ma, ce qui est bien pour le traitement mais illisible \u00e0 l'\u00e9cran.<\/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\">Quatre \u00e9v\u00e9nements instantan\u00e9s apparaissent imm\u00e9diatement, un par transaction existante, chacun avec <code>\"op\":\" lectura\"<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Maintenant, g\u00e9n\u00e9rez une modification en direct sur 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 nouvel \u00e9v\u00e9nement appara\u00eet avec <code>\"op\":\"c\"<\/code> et la rang\u00e9e compl\u00e8te dans <code>apr\u00e8s<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une mise \u00e0 jour montre <code>\"op\":\"u\"<\/code> avec les deux <code>avant<\/code> et <code>apr\u00e8s<\/code> peupl\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une suppression montre <code>\"op\":\"d\"<\/code> avec <code>avant<\/code> peupl\u00e9 et <code>apr\u00e8s<\/code> nul.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Latence inf\u00e9rieure \u00e0 la seconde en r\u00e9gime stable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-6-set-up-the-postgre-sql-sink\">\u00c9tape 6 \u2014 Configurer le Sink PostgreSQL<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On <code>srv2<\/code>, cr\u00e9ez l'utilisateur cible, la base de donn\u00e9es et le sch\u00e9ma, et autorisez les connexions \u00e0 distance depuis <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\">Modifier les deux fichiers de configuration PostgreSQL sur <code>srv2<\/code>.\n<code>listen_addresses = '*'<\/code> indique \u00e0 PostgreSQL d'accepter les connexions sur toutes les interfaces r\u00e9seau, pas seulement sur localhost.\nLe <code>pg_hba.conf<\/code> ligne autorise tout utilisateur et base de donn\u00e9es \u00e0 partir du <code>192.168.0.0\/24<\/code> sous-r\u00e9seau pour s'authentifier avec SCRAM (le protocole de hachage s\u00e9curis\u00e9 des mots de passe), qui est la mani\u00e8re dont le connecteur sink JDBC de Debezium acc\u00e9dera \u00e0 la base de donn\u00e9es.<\/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\">On <code>srvdebezium<\/code>, enregistrez le connecteur sink JDBC.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le <code>RegexRouter<\/code> Les bandes SMT enl\u00e8vent <code>oracle.BANQUE.<\/code> pr\u00e9fixe de chaque sujet avant qu'il ne devienne le nom de la table cible, donc <code>oracle.BANQUE.TRANSACTIONS<\/code> cartes vers <code>transactions bancaires<\/code> plut\u00f4t que <code>banque.oracle_banque_transactions<\/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\">Quelques secondes apr\u00e8s l'enregistrement, les rang\u00e9es d'instantan\u00e9s apparaissent dans PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les 18 lignes de BANKING se r\u00e9pliquent en moins d'une minute.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Toute modification ult\u00e9rieure sur Oracle se propage \u00e0 PostgreSQL en quelques secondes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-7-add-a-new-table-mid-stream\">\u00c9tape 7 \u2014 Ajouter une nouvelle table en cours de route<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">C'est l\u00e0 que le pipeline fait ses preuves.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ajouter un <code>PR\u00caTS<\/code> table vers BANQUE pendant que la r\u00e9plication est en cours.<\/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\">Le connecteur Oracle r\u00e9cup\u00e8re la nouvelle table car elle correspond <code>BANQUE<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un nouveau sujet <code>oracle.BANQUE.PRETS<\/code> appara\u00eet dans Kafka.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le collecteur JDBC d\u00e9tecte ce topic car il correspond <code>oracle\\.BANKING\\..+<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une nouvelle table <code>banque.pr\u00eats<\/code> est cr\u00e9\u00e9 dans PostgreSQL avec les trois lignes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aucune reconfiguration du connecteur.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pas de red\u00e9marrage.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pas de red\u00e9ploiement.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-schema-evolution-tradeoff\">Le compromis de \u00e9volution du sch\u00e9ma<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>schema.evolution: basique<\/code> est ce qui a rendu la cr\u00e9ation automatique de <code>banque.pr\u00eats<\/code> fonctionne sans intervention manuelle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C'est aussi ce qui disqualifie cette configuration pour la production.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ex\u00e9cuter <code>comptes bancaires<\/code> sur PostgreSQL apr\u00e8s r\u00e9plication avec cette configuration et le tableau ressemble \u00e0 ceci :<\/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\">Les types num\u00e9riques et de date se reproduisent exactement : <code>grand entier<\/code>, <code>entier<\/code>, <code>num\u00e9rique(15,2)<\/code>, <code>horodatage(6)<\/code>. Les contraintes NOT NULL sur les colonnes num\u00e9riques sont conserv\u00e9es.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les colonnes de cha\u00eenes de caract\u00e8res atterrissent toujours en tant que <code>texte<\/code> quelle que soit la longueur de la source. Le r\u00e9cepteur JDBC traite <code>texte<\/code> et <code>varchar<\/code> aussi fonctionnellement \u00e9quivalentes \u2014 m\u00eame stockage, m\u00eame performance \u2014 donc il choisit <code>texte<\/code> inconditionnellement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>O\u00d9 customer_id = 1001<\/code> fonctionne maintenant sans guillemets. <code>\u00e9quilibre<\/code> correctement <code>num\u00e9rique(15,2)<\/code>. La reproduction est fid\u00e8le au type 80%, \u00e0 peu pr\u00e8s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L'\u00e9cart restant \u2014 les contraintes de longueur VARCHAR \u2014 d\u00e9pend de l'approche que vous choisissez parmi les trois suivantes :<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Configuration<\/th><th>Types num\u00e9riques<\/th><th>Types de cha\u00eenes<\/th><th>Fid\u00e9lit\u00e9<\/th><\/tr><\/thead><tbody><tr><td>Non <code>datatype.propagate.source.type<\/code><\/td><td><code>texte<\/code> partout<\/td><td><code>texte<\/code> partout<\/td><td>~0%<\/td><\/tr><tr><td><code>datatype.propagate.source.type<\/code> d\u00e9finir (ce laboratoire)<\/td><td>exact \u2014 bigint, entier, num\u00e9rique(p,s)<\/td><td><code>texte<\/code>, longueur perdue<\/td><td>~80%<\/td><\/tr><tr><td>ora2pg sch\u00e9ma + <code>sch\u00e9ma.\u00e9volution<\/code> d\u00e9sactiv\u00e9<\/td><td>exact<\/td><td>exact \u2014 VARCHAR(n) conserv\u00e9<\/td><td>100%<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Pour une fid\u00e9lit\u00e9 compl\u00e8te en production :<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Construisez le sch\u00e9ma PostgreSQL \u00e0 l'avance en utilisant ora2pg.<br>Le sch\u00e9ma obtient des types exacts : <code>NUMERIC(15,2)<\/code> pour les montants, <code>ENTIER<\/code> pour les identifiants, appropri\u00e9 <code>VARCHAR(n)<\/code> longueurs.<\/li>\n\n\n\n<li>D\u00e9sactiver <code>sch\u00e9ma.\u00e9volution<\/code> dans le raccord d'\u00e9vier.<br>Debezium diffuse les donn\u00e9es dans le sch\u00e9ma existant sans le modifier.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">La fonctionnalit\u00e9 d'\u00e9volution de sch\u00e9ma est destin\u00e9e aux laboratoires et aux prototypes \u2014 utile pour prouver que le pipeline fonctionne, insuffisante pour la production.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-new-table-discovery-delay\">Le d\u00e9lai de d\u00e9couverte de nouvelle table<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Lorsqu'une nouvelle table appara\u00eet dans Oracle pendant que la r\u00e9plication est en cours, il y a un l\u00e9ger mais r\u00e9el d\u00e9lai avant qu'elle n'apparaisse dans PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le premier <code>S\u00c9LECTIONNER<\/code> sur la nouvelle table PostgreSQL peut retourner <code>la relation n'existe pas<\/code> m\u00eame si le topic Kafka est d\u00e9j\u00e0 rempli.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La s\u00e9quence est :<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Oracle valide le DDL \u2014 la nouvelle table appara\u00eet dans le journal de transactions.<\/li>\n\n\n\n<li>Debezium capture la DDL et cr\u00e9e le topic Kafka.<br>\u00c0 ce stade, le sujet existe, mais le connecteur d'extraction ne l'a pas encore vu.<\/li>\n\n\n\n<li>Les INSERTs publient sur le nouveau sujet.<\/li>\n\n\n\n<li>Le connecteur d'\u00e9vier rafra\u00eechit \u00e9ventuellement ses m\u00e9tadonn\u00e9es de rubrique.<br>Le d\u00e9faut <code>metadata.max.age.ms<\/code> prend 5 minutes, bien que Connect se rafra\u00eechisse g\u00e9n\u00e9ralement plus souvent.<br>Lorsqu'il le fait, le puits d\u00e9couvre le nouveau sujet, lit le sch\u00e9ma du premier \u00e9v\u00e9nement, puis ex\u00e9cute <code>CR\u00c9ER UNE TABLE<\/code> sur PostgreSQL via <code>schema.evolution: basique<\/code>, puis applique les \u00e9v\u00e9nements.<\/li>\n\n\n\n<li>La table existe maintenant sur PostgreSQL.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">D\u00e9lai de d\u00e9couverte de bout en bout typique : 5 \u00e0 30 secondes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dans une migration r\u00e9elle, cela importe si une application est cens\u00e9e \u00e9crire dans une toute nouvelle table Oracle et la lire imm\u00e9diatement depuis PostgreSQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deux fa\u00e7ons d'y faire face :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Plus bas <code>consumer.override.metadata.max.age.ms<\/code> pour une d\u00e9couverte plus rapide.<\/li>\n\n\n\n<li>Pr\u00e9-cr\u00e9er les tables dans PostgreSQL avec les types corrects et d\u00e9sactiver <code>sch\u00e9ma.\u00e9volution<\/code> \u2014 le mod\u00e8le de production recommand\u00e9 ci-dessus.<br>Les tables existent avant que les donn\u00e9es ne circulent.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"production-gotchas\">Probl\u00e8mes de production<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quelques points \u00e0 surveiller en production que le laboratoire ne r\u00e9v\u00e8le pas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Tables sans cl\u00e9s primaires.<\/strong><br>Les \u00e9v\u00e9nements UPDATE et DELETE de Debezium sont index\u00e9s par la cl\u00e9 primaire.<br>Les tables sans cl\u00e9 primaire peuvent \u00eatre instantan\u00e9es et les insertions r\u00e9pliqu\u00e9es, mais les mises \u00e0 jour et les suppressions ne peuvent pas \u00eatre appliqu\u00e9es \u00e0 PostgreSQL.<br>Auditez le sch\u00e9ma source pour les tables sans cl\u00e9 primaire avant de cadrer la migration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>R\u00e9tention des journaux d'archive LogMiner.<\/strong><br>LogMiner ne peut analyser que les journaux d'archives qui existent toujours sur le disque.<br>Si votre politique de r\u00e9tention supprime les journaux d'archives apr\u00e8s 24 heures et que Debezium prend du retard de plus que cela, le pipeline ne peut pas reprendre l\u00e0 o\u00f9 il s'est arr\u00eat\u00e9.<br>Dimensionnez la r\u00e9tention des journaux d'archives \u00e0 votre pire sc\u00e9nario de temps d'arr\u00eat Debezium \u2014 multipliez votre r\u00e9tention habituelle par 3 pour des raisons de s\u00e9curit\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Persistance de conteneur.<\/strong><br>Course \u00e0 pied <code>docker run -it<\/code> attache le conteneur au terminal.<br>Fermer le terminal arr\u00eate le conteneur et interrompt le pipeline.<br>Utilisation <code>-d<\/code> (d\u00e9tach\u00e9) pour tout ce qui dure plus longtemps qu'un test manuel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Cr\u00e9dits de groupe de consommateurs.<\/strong><br>La suppression d'un connecteur Sink et sa recr\u00e9ation sous le m\u00eame nom ne r\u00e9initialise pas sa position dans Kafka.<br>Le nouveau connecteur reprend \u00e0 l'offset pr\u00e9c\u00e9demment valid\u00e9, en ignorant tous les \u00e9v\u00e9nements mis en file d'attente entre-temps.<br>R\u00e9initialiser explicitement les d\u00e9calages avec <code>kafka-consumer-groups.sh --reset-offsets --to-earliest<\/code> avant de vous r\u00e9inscrire.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Nom du sch\u00e9ma en casse.<\/strong><br>Les noms de sch\u00e9mas Oracle sont en majuscules par d\u00e9faut.<br>Le <code>schema.include.liste<\/code> la valeur dans la configuration du connecteur doit correspondre exactement \u2014 <code>LA BANQUE<\/code>, pas <code>banque<\/code>.<br>Les noms de sujets de type \"sink-side\" suivent le format Oracle, c'est pourquoi <code>RegexRouter<\/code> enl\u00e8ve un pr\u00e9fixe en majuscules.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"frequently-asked-questions\">Foire aux questions<\/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>Debezium peut-il migrer le sch\u00e9ma Oracle, ou seulement les donn\u00e9es ?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>Seulement les donn\u00e9es.<br \/>Debezium capture les \u00e9v\u00e9nements DML \u2014 INSERT, UPDATE, DELETE \u2014 du journal de redo d'Oracle.<br \/>Les d\u00e9finitions de sch\u00e9mas, les proc\u00e9dures stock\u00e9es, les d\u00e9clencheurs, les vues et les s\u00e9quences doivent \u00eatre migr\u00e9s s\u00e9par\u00e9ment, g\u00e9n\u00e9ralement avec ora2pg.<br \/>Le sch\u00e9ma.\u00e9volution : la fonctionnalit\u00e9 de base peut auto-cr\u00e9er les tables cibles mais avec des types simplifi\u00e9s et sans contraintes \u2014 tant mieux pour les laboratoires, mais pas pour la production.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1777800042851\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>Quelle est la latence entre Oracle et PostgreSQL ?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>Quelques secondes en r\u00e9gime permanent sur une configuration LogMiner saine.<br \/>La latence augmente sous une forte charge d'\u00e9criture ou si la g\u00e9n\u00e9ration des journaux d'archivage d\u00e9passe le taux de lecture de LogMiner.<br \/>Planifiez un d\u00e9calage inf\u00e9rieur \u00e0 une minute et surveillez-le explicitement avec 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>LogMiner est-il obsol\u00e8te dans Oracle 19c ou 21c ?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>LogMiner est le m\u00e9canisme CDC pris en charge dans Oracle 19c.<br \/>Dans Oracle 21c et versions ult\u00e9rieures, Oracle a introduit XStream comme alternative payante.<br \/>Debezium continue de prendre en charge les deux : l'adaptateur LogMiner est celui par d\u00e9faut et fonctionne sans licence Oracle suppl\u00e9mentaire.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1777800044851\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>Est-ce que cela peut s'ex\u00e9cuter contre Oracle Standard Edition ?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>Oui, avec des r\u00e9serves.<br \/>LogMiner est disponible en Standard Edition 2, mais le journalisation suppl\u00e9mentaire et le mode archivelog sont requis, et certaines optimisations disponibles en Enterprise Edition ne le sont pas.<br \/>Testez par rapport \u00e0 l'\u00e9dition exacte que vous poss\u00e9dez.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1777800045851\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>Puis-je utiliser Debezium pour une migration ponctuelle sans le maintenir en fonctionnement ?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>Oui.<br \/>Vous pouvez ex\u00e9cuter la phase d'instantan\u00e9, attendre qu'elle se termine, puis arr\u00eater le connecteur.<br \/>Mais c'est rarement la bonne d\u00e9cision \u2014 si vous allez configurer Debezium, autant le laisser diffuser jusqu'au basculement afin d'avoir un pipeline test\u00e9 et sans latence pr\u00eat lorsque l'application sera redirig\u00e9e.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"in-summary\">En r\u00e9sum\u00e9<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium transforme une migration d'Oracle vers PostgreSQL d'une simple op\u00e9ration de vidage et restauration en un pipeline contr\u00f4lable et observable que vous pouvez ex\u00e9cuter pendant des semaines avant la bascule.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">LogMiner lit le redo log d'Oracle, Kafka d\u00e9tient le flux d'\u00e9v\u00e9nements et le sink JDBC applique les modifications \u00e0 PostgreSQL avec une latence inf\u00e9rieure \u00e0 la minute.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le r\u00e9sultat est un canal de r\u00e9plication en temps r\u00e9el que vous pouvez valider, rejouer et annuler proprement \u2014 la base de toute migration avec interruption nulle ou quasi nulle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si vous pr\u00e9voyez une migration d'Oracle vers PostgreSQL et souhaitez de l'aide pour concevoir la phase de basculement ou r\u00e9viser votre architecture CDC, <a href=\"https:\/\/rootfan.com\/fr\/services\/\">prendre contact \u2192<\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>TL;DR: Debezium reads Oracle&#8217;s redo logs through LogMiner, publishes every change as a Kafka event, and a JDBC sink connector applies those events to PostgreSQL in real time.The result is a transparent, replayable replication pipeline you can run for weeks before cutover \u2014 useful for any Oracle to PostgreSQL migration that needs zero or near-zero &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/rootfan.com\/fr\/oracle-to-postgresql-replication-debezium\/\" class=\"more-link\">Continuer la lecture<span class=\"screen-reader-text\"> de &laquo;&nbsp;How to Set Up Real-Time Oracle to PostgreSQL Replication Using Debezium&nbsp;&raquo;<\/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\/fr\/wp-json\/wp\/v2\/posts\/6832","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/comments?post=6832"}],"version-history":[{"count":12,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/posts\/6832\/revisions"}],"predecessor-version":[{"id":6846,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/posts\/6832\/revisions\/6846"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/media\/6833"}],"wp:attachment":[{"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/media?parent=6832"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/categories?post=6832"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/tags?post=6832"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}