Introduction

Streams n'a pas une très bonne réputation auprès des DBA Oracle, toutefois si l'on souhaite mettre de manière simple et peu onéreuse une réplication de données entre deux serveurs, cette solution n'est pas dénuée d'intérêt.

Oracle déconseille Streams au profit de GoldenGate ( coût supplémentaire ). Streams ne demande pas de licence ce qui est intéressant pour les petits budgets. L'objectif ici est de présenter la mise en place d'une réplication STREAMS entre deux bases Oracle 12c. L'exemple porte sur la réplication bi-directionnelle d'un schéma nommé ALICE entre deux bases de données RED et BLACK.

La base RED est sur le serveur srv1 et la base BLACK sur srv2. La version de Oracle est la 12.1.0.2. L'exemple est toutefois applicable dans les versions antérieures.

replication_streams_red_black_blog.jpg

La gestion de streams est un livre à elle seule, cet exemple ne montre pas toutes les possibilités, notamment la gestion des conflits.

Principe de STREAMS

Le principe est simple :

  • Capture des ordres sur la base source
  • Propagation vers la base cible
  • Application des ordres sur la base cible.

Les composants STREAMS à mettre en place sont :

  • File ou queue d'envoi et de réception
  • Processus de capture, de propagation et d'application
  • Database links
  • User dédié à la supervision STREAMS -> strmadmin

Pré-requis

Les bases doivent être en archivelog, le supplemental logging minimum en place et activer le mode force logging. Les requêtes suivantes doivent être passées sur les deux bases RED et BLACK.

select log_mode from v$database;

LOG_MODE
------------------------------------
ARCHIVELOG

select supplemental_log_data_min Min, supplemental_log_data_pk PK, 
supplemental_log_data_ui UI, supplemental_log_data_fk FK from v$database; 

MIN            PK        UI        FK
-------------- --------- --------- ---------
NO             NO        NO        NO

alter database add supplemental log data;

select supplemental_log_data_min Min, supplemental_log_data_pk PK, 
supplemental_log_data_ui UI, supplemental_log_data_fk FK from v$database;

MIN            PK        UI        FK
-------------- --------- --------- ---------
YES            NO        NO        NO

select force_logging from v$database;

FORCE_LOGGING
---------------
NO

alter database force logging;

select force_logging from v$database;

FORCE_LOGGING
---------------
YES

User dédié STREAMS

Il est conseillé de créer un compte avec les droits DBA chargé de la supervision STREAMS. Ces actions sont à effectuer sur les deux bases.

create tablespace data_streams;
create temporary tablespace temp_streams;
create user strmadmin identified by strmadmin default tablespace data_streams temporary tablespace temp_streams 
quota unlimited on data_streams;
grant dba, select_catalog_role to strmadmin;
execute dbms_streams_auth.grant_admin_privilege('STRMADMIN',TRUE);

Configuration Oracle*Net

C'est un élément important dans la réplication STREAMS. Outre la résolution correcte via tnsnames il faut qu'au niveau de chaque base le paramètre global_names soit à TRUE.

show parameter global_names;

NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
global_names                         boolean                           FALSE

alter system set global_names=true scope=both;

show parameter global_names;

NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
global_names                         boolean                           TRUE

Création des DBLINKS

La création des database links doit être rigoureuse. Il est impératif que le nom des DBLINKS soit identique au nom de la base de données en raison de global_names = true.

Base BLACK, connexion en strmadmin

create database link red connect to strmadmin identified by strmadmin using 'RED';

select * from global_name@red;

GLOBAL_NAME
--------------
RED

Base RED, connexion en strmadmin

create database link black connect to strmadmin identified by strmadmin using 'BLACK';

select * from global_name@black;

GLOBAL_NAME
--------------
BLACK

Ci dessous la configuration tnsnames.ora

RED=
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = srv1)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = RED)
    )
  )

BLACK=
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = srv2)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = BLACK)
    )
  )

Création du user ALICE

A faire sur les deux bases RED et BLACK.

create tablespace data_alice;
create temporary tablespace temp_alice;
create user alice identified by ecila default tablespace data_alice temporary tablespace temp_alice quota unlimited on data_alice;
grant connect,resource to alice;

Uniquement sur la base RED créer des données pour le user ALICE

connect alice/ecila
create table compositeur ( nocomp number(2), prenom varchar2(15 char), nom varchar2(15 char ), naissance date, deces date, constraint pk_compositeur primary key (nocomp) );
insert into compositeur values( 1,'Frédéric','Chopin', to_date('01/03/1810','DD/MM/YYYY'),to_date('17/10/1849','DD/MM/YYYY'));
insert into compositeur values( 2,'Hector','Berlioz', to_date('11/12/1803','DD/MM/YYYY'),to_date('08/03/1869','DD/MM/YYYY'));
insert into compositeur values( 3,'Franz','Liszt', to_date('22/10/1811','DD/MM/YYYY'),to_date('31/07/1886','DD/MM/YYYY'));
insert into compositeur values( 4,'Michel','Legrand', to_date('24/02/1932','DD/MM/YYYY'),null);
insert into compositeur values( 5,'Marin','Marais', to_date('31/05/1656','DD/MM/YYYY'),to_date('15/08/1728','DD/MM/YYYY'));
insert into compositeur values( 6,'Pierre','Boulez', to_date('26/03/1925','DD/MM/YYYY'),to_date('05/01/2016','DD/MM/YYYY'));
insert into compositeur values( 7,'Jean-Michel','Jarre', to_date('24/08/1948','DD/MM/YYYY'),null);
commit;

A ce stade la configuration de base et les pré-requis sont en place. Les manipulations suivantes doivent se faire de manière symétrique, en modifiant certaines clauses, sur les deux bases. Pour toutes les actions utiliser le compte STRMADMIN et non SYSDBA.

Réplication STREAMS du schéma ALICE

Le diagramme suivant présente l'architecture générale de la réplication bi-directionnelle.

red_black_streams.jpg

Du fait de la symétrie de la réplication, il faut être attentif lors des créations des éléments streams à bien spécifier les clauses qui diffèrent.

Création des queues

Se connecter strmadmin/strmadmin sur chaque base avant la création des queues. Les requêtes sont identiques sur les deux bases.

Création de la file d'envoi.

begin
  dbms_streams_adm.set_up_queue(
  queue_table => 'ENVOI_ALICE',
  queue_name => 'ENVOI_ALICE',
  queue_user => 'STRMADMIN');
end;
/

Création de la file de réception

begin
  dbms_streams_adm.set_up_queue(
  queue_table => 'RECEPTION_ALICE',
  queue_name => 'RECEPTION_ALICE',
  queue_user => 'STRMADMIN');
end;
/

Vérification

select name from dba_queues where owner='STRMADMIN'

NAME
------------------------------
RECEPTION_ALICE
AQ$_RECEPTION_ALICE_E
ENVOI_ALICE
AQ$_ENVOI_ALICE_E

Les queues type AQ$ sont automatiquement mises en place pour gérer les exceptions ( non utilisées dans ce test ).

Création des captures

Les captures sont à mettre en place sur les deux bases. Attention certaines clauses diffèrent selon la base concernée.

Base RED

La capture se fait sur le schéma ALICE.

begin
  dbms_streams_adm.add_schema_rules(
    schema_name => 'ALICE',
    streams_type => 'capture',
    streams_name => 'CAPTURE_ALICE',
    queue_name => 'ENVOI_ALICE',
    include_dml => true,
    include_ddl => true,
    inclusion_rule => true);
  end;
/

Base BLACK

La création est identique à la précédente, sauf la clause inclusion_rule.

begin
  dbms_streams_adm.add_schema_rules(
    schema_name => 'ALICE',
    streams_type => 'capture',
    streams_name => 'CAPTURE_ALICE',
    queue_name => 'ENVOI_ALICE',
    include_dml => true,
    include_ddl => true );
  end;
/

Création des propagations

Une fois les captures faites, il faut envoyer les informations STREAMS vers l'autre base. Cette action est gérée par les propagations. Suite à leur création les propagations sont automatiquement démarrées. Depuis la base RED on envoie vers la queue de réception ( RECEPTION_ALICE ) de la base BLACK et réciproquement.

Base RED

begin
  dbms_streams_adm.add_schema_propagation_rules(
    schema_name => 'ALICE',
    streams_name => 'prop_red_to_black',
    source_queue_name => 'ENVOI_ALICE',
    destination_queue_name => 'RECEPTION_ALICE@BLACK',
    include_dml => true,
    include_ddl => true,
    source_database => 'RED');
end;
/

Base BLACK

begin
  dbms_streams_adm.add_schema_propagation_rules(
    schema_name => 'ALICE',
    streams_name => 'prop_black_to_red',
    source_queue_name => 'ENVOI_ALICE',
    destination_queue_name => 'RECEPTION_ALICE@RED',
    include_dml => true,
    include_ddl => true,
    source_database => 'BLACK');
end;
/

Création des applications

Une fois les données reçues sur la base de destination il faut les appliquer.

Base RED

begin
  dbms_streams_adm.add_schema_rules(
    schema_name=>'ALICE',
    streams_type=>'apply',
    streams_name=>'APPLY_ALICE',
    queue_name=>'RECEPTION_ALICE',
    include_dml=>true,
    include_ddl=>true,
    source_database=>'BLACK'
  );
end;
/

Il peut être intéressant de ne pas stopper le processus d'application en cas d'erreur, ce qui est la configuration par défaut. Procéder ainsi :

begin
  dbms_apply_adm.set_parameter(
    apply_name  => 'APPLY_ALICE', 
    parameter   => 'disable_on_error', 
    value       => 'n');
end;
/

Base BLACK

begin
  dbms_streams_adm.add_schema_rules(
    schema_name=>'ALICE',
    streams_type=>'apply',
    streams_name=>'APPLY_ALICE',
    queue_name=>'RECEPTION_ALICE',
    include_dml=>true,
    include_ddl=>true,
    source_database=>'RED'
  );
end;
/

Il peut être intéressant de ne pas stopper le processus d'application en cas d'erreur, ce qui est la configuration par défaut. Procéder ainsi :

begin
  dbms_apply_adm.set_parameter(
    apply_name  => 'APPLY_ALICE', 
    parameter   => 'disable_on_error', 
    value       => 'n');
end;
/

Instanciation de RED

La dernière étape consiste à l'instanciation de RED sur BLACK. Cette requête doit être lancée sur la base BLACK.

set serveroutput on size 1000000
declare
  current_scn number;
begin
  current_scn:=dbms_flashback.get_system_change_number();
  dbms_output.put_line( 'SCN Courant : ' || current_scn );
  dbms_apply_adm.set_schema_instantiation_scn@RED(
    source_schema_name => 'ALICE',
    source_database_name => 'BLACK',
    instantiation_scn => current_scn,
    recursive => true
  );
end;
/

Importation des données Alice de RED vers BLACK

Utilisation via datapump. Sur chaque serveur créer un répertoire /home/oracle/dpump. Sur chaque base créer un object directory dpump

create directory dpump as '/home/oracle/dpump';

L'import datapump dans BLACK se fera via un dblink -> RED. Dans un premier temps relever le numéro SCN de la base RED

select dbms_flashback.get_system_change_number() from dual;

74246358

Ce numéro doit être utilisé par l'import datapump.Sur la base BLACK lancer l'import datapump ainsi

impdp strmadmin/strmadmin@black directory=dpump network_link=red logfile=alice.dpimp.log table_exists_action=replace schemas=alice flashback_scn=74246358

Démarrage du STREAMS

Sur la base BLACK, démarrer l'apply et la capture

exec dbms_apply_adm.start_apply(apply_name=>'APPLY_ALICE');
exec dbms_capture_adm.start_capture(capture_name=>'CAPTURE_ALICE');

Sur la base RED démarrer l'apply et la capture.

exec dbms_apply_adm.start_apply(apply_name=>'APPLY_ALICE');
exec dbms_capture_adm.start_capture(capture_name=>'CAPTURE_ALICE');

Test

Se connecter à RED en alice et insérer la ligne suivante :

insert into compositeur values(8,'Maurice','Jarre',null,null);
commit;

Visualiser sur BLACK en connection alice la presence de l'insertion

Se connecter à BLACK en alice et faire un update

update compositeur set naissance=to_date('13/09/1924','DD/MM/YYYY') where nocomp=8;
commit;

Se reconnecter à RED pour voir la modification, puis faire un autre update

update compositeur set deces=to_date('28/03/2009','DD/MM/YYYY') where nocomp=8;
commit;

Les deux bases doivent être synchronisées.

Conclusion

La mise en place n'est pas très complexe, il faut juste être attentif à la symétrie. L'avantage c'est que les mises à jours peuvent se faire sur l'une ou l'autre des bases.