Migrer de 10g en 11g avec Zéro Indisponibilité et Streams

Ou disons… Avec moins de 5 minutes d’indisponibilité ! Bien sur il y a quelques restrictions…

D’abord merci à Wilfrid qui a posé cette question sur un post précédent que j’ai malheureusement du effacer. Et, si vous voulez mettre en œuvre Streams pour un projet autre qu’une simple mise à jour de logiciel, je sais avec qui vous pouvez travailler !

1. Le principe

L’idée consiste à construire une seconde base de données comme décrit dans le schéma ci-dessous. La source et la destination doivent être des versions Enterprise Edition. La base clone est dans une version supérieure à la source. Cette architecture limite les interventions et la charge sur la base primaire au minimum:

Les 3 grandes phases du fonctionnement de l’architecture nécessitent de :

  1. Transmettre les fichiers d’archivelog du serveur primaire à celui abritant le clone et les enregistrer ces fichiers dans la base clone. Vous pouvez réaliser ces opérations manuellement ou en utilisant les capacités de transport des logs d’Oracle Enterprise Edition.
  2. Capturer les modifications du schéma applicatif au moyen d’un processus de capture Streams et de les publier dans une buffered queue Oracle Advanced Queuing
  3. Appliquer les modifications sur le schéma de la base clone.

Tous les détails, ou presque de cette implémentation sont dans la documentation qui suit :

Oracle® Streams Concepts and Administration
11g Release 1 (11.1)
B Online Database Upgrade with Oracle Streams

Nous allons simplement illustrer ce chapitre…

2. Un exemple simple

Alors disons pour commencer que vous avez une base de données, nommée BLACK en version 10.2. Il s’agit d’une base de données dont l’instance associée est nommée également BLACK ; pour cet exemple, il s’agit d’une base Oracle 10.2.0.4 sur Linux x86. Cette base a un schéma applicatif DEMO comme celui ci-dessous :

connect / as sysdba

create user demo
identified by demo
default tablespace users
temporary tablespace temp;

grant connect, resource to demo;

connect demo/demo

create table t_demo1 (id number primary key,
lib varchar2(10));

create table t_demo2 (id number,
lib varchar2(10));

create or replace type typ_demo3
as object(lib varchar2(100));
/

create table t_demo3 (id number primary key,
lib typ_demo3);

insert into demo.t_demo1
values (1,'data 1');

insert into demo.t_demo1
values (2,'data 2');

insert into demo.t_demo2
values (1,'data 1');

commit;

3. Quelques restrictions

Autant annoncer les mauvaises nouvelles d’abord ; Comme pour une logical Standby database, Streams ne permet pas de répliquer n’importe quelle structures :

  • Certain nombre de types, comme les SecureFiles, les « User Defined Types », les ROWID ou les Types Spatial ne peuvent pas être répliqués.
  • Certaines structures comme les files d’attentes AQ ne peuvent pas être répliquées.
  • Les tables répliquées doivent avoir au moins une clé primaire ou une clé unique.
  • Toutes les modifications doivent être stockées dans redo logs. En fait, il faudra activer des niveaux de log supplémentaires

Si votre base de données ne respecte pas ces points précédents, il vous faudra trouver un moyen de contourner votre problème, avec, par exemple des triggers. Vous noterez également que certaines structures ne nécessitent pas d’être répliquées :

  • Les Global Temporary Tables, évidemment !
  • Les Materialized Views
  • Les triggers devront généralement, mais aussi selon la logique qu’ils contiennent être désactivés
  • Les Jobs devront également le plus souvent être désactivés, sauf dans certains cas comme les rafraichissements des Materialized Views qui ceux-ci sont programmés.
  • Les valeurs utilisées des séquences ne sont pas répliquées

Ceci dit, passons à la pratique !

4. Préparer la base de données primaire

Pour pouvoir mettre en oeuvre Streams, il faut que votre base de données primaire soit en mode archivelogs. En outre, vous devrez sans doute ajouter un niveau de log supplémentaire :

alter database add supplemental log
data (primary key, unique index) columns;

select SUPPLEMENTAL_LOG_DATA_MIN,
SUPPLEMENTAL_LOG_DATA_PK,
SUPPLEMENTAL_LOG_DATA_UI
from gv$database;

Ensuite, vous devrez instancier la base clone. Utiliser une sauvegarde est, sans doute, la méthode la plus rapide. Vous pourriez alors utiliser une sauvegarde disque avec RMAN. Il suffit que vous ayez un répertoire avec le même nom sur les 2 serveurs primaire et clone (disons /backup) ; Ci-dessous, un exemple de sauvegarde RMAN ; notez le numéro de séquence du dernier fichier archivé pour commencer :

sqlplus / as sysdba

select thread#, max(sequence#) last_seq
from v$archived_log
group by thread#;

THREAD# LAST_SEQ
------- --------
1 2

exit

rman target /

configure controlfile autobackup
format for device type disk to '/oradata/backup/%F';

configure controlfile autobackup on;

configure channel device type disk
format '/oradata/backup/%U';

configure device type disk
backup type to compressed backupset parallelism 1;

show all;

backup database;

exit;

Il faut ensuite enregistrer le dictionaire de la base de données primaire dans les redo log et noter le SCN correspondant comme le premier SCN du process (first_scn) de la capture Streams :

sqlplus / as sysdba

SET SERVEROUTPUT ON
DECLARE
scn NUMBER;
BEGIN
DBMS_CAPTURE_ADM.BUILD(
first_scn => scn);
DBMS_OUTPUT.PUT_LINE('First SCN Value = ' || scn);
END;
/

First SCN Value = 497637

Une fois dictionnaire de la base primaire dans les redo log il faut indiquer les tables qui seront répliquées par Oracle Streams avec une des procédures dbms_capture_adm.prepare_xxx_instantiation comme ci-dessous :

exec dbms_capture_adm.prepare_schema_instantiation(-
schema_name=>'demo');

Si vous tentez d’exécuter cette procédure sur une base Standard Edition 10g ou 11g, vous obtenez le message qui suit:

BEGIN dbms_capture_adm.prepare_schema_instantiation(...
*
ERROR at line 1:
ORA-00439: feature not enabled: Streams Capture
ORA-06512: at "SYS.DBMS_LOGREP_UTIL", line 710
[...]

Il faudra archiver le dernier fichier de log pour s’assurer que le SCN de démarrage de l’apply soit postérieur à la construction du Multi Version Data Dictionary (MVDD) . Notez également le dernier fichier d’archive que
vous sauvegarderez :


sqlplus / as sysdba

alter system archive log current;

col next_change# format 999999999999
select thread#, max(sequence#) last_seq, max(next_change#) instantiation_scn
from v$archived_log
group by thread#;

THREAD# LAST_SEQ INSTANTIATION_SCN
------- -------- -----------------
1 7 498370

exit

rman target /

backup archivelog from sequence 2;

Notez le derniers SCN du fichier d’archive que nous allons appliquer sur le clone. Ce SCN sera le SCN utilisé pour demarrer le processus d’apply. Enfin, partagez la sauvegarde avec le serveur clone par la méthode de votre choix (ftp, scp, nfs, bandes…) :

cd /backup

scp * oracle@clone-server:/backup

5. Préparer la base clone

Là aussi, vous avez le choix de la méthode. RMAN duplicate est une méthode simple et j’en ai déjà parlé dans d’autres circonstances. Pour l’implémenter :

  • Installez la même version d’Oracle que sur la base primaire
  • Créez un listener sur le serveur clone avec un enregistrement statique de l’instance pour permettre une connection à distance, même si l’instance n’est pas démarrée
  • Créez un fichier de mot de passe sur le serveur clone pour permettre la connexion à distance pour un utilisateur SYSDBA
  • Utilisez le fichier init.ora ou spfile de l’instance primaire pour créer et démarrer l’instance clone que nous appellerons WHITE pour cet exemple. Paramètrez log_file_name_convert et db_file_name_convert, si vous voulez changer la structure de répertoires de la base de données clone
  • Si vous êtes en 10g, créez l’arborescence d’aministration de votre base de données (adump, bdump, cdump, udump…) ainsi que les repertoires pour votre base de données et les fichiers d’archivelog
  • Créez un alias pour la base primaire et pour le clone dans les fichiers tnsnames.ora des 2 serveurs

Une fois l’ensemble des pré-réquis mis en place, lancez RMAN et la commande duplicate comme ci-dessous :

rman target sys@black auxiliary sys@white

startup clone nomount;

DUPLICATE TARGET DATABASE
TO WHITE nofilenamecheck
until sequence 8
open restricted;

Dans l’exemple qui précède, le numéro de séquence utilisé est le premier qui ne fait pas partie de la sauvegarde. « open restricted » permet de prévenir l’exécution des jobs que vous pouvez par conséquent stopper. N’oubliez pas de désactiver les triggers ! Vous pouvez ensuite ouvrir l’instance normalement :

sql 'alter system disable restricted session';

La base clone est créée.

6. Mettre à jour votre base de données

L’idée est d’appliquer un Patch Set sur la base clone ou de transformer votre schéma cible en fonction de vos besoins. Ces mises peuvent être effectuées la base clone sans impacter la base primaire; Par exemple, vous pouvez installer une Oracle 11g et lancer DBUA sur la base clone comme ci-dessous :

dbua -silent -sid WHITE

Vous appliquerez les changements enregistrés dans les redo logs de la base primaire dans les étapes qui suivent…

7. Créer les process de capture et d’apply sur la base clone

Avant de créer les process, il faut créer l’administrateur Streams, strmadmin, ainsi que la file d’attentes AQ qui sera utilisée par les process de capture et apply sur la base clone. Ci-dessous un exemple de comment réaliser ces opérations :

CREATE TABLESPACE streams_tbs
DATAFILE '/oradata/WHITE/streams_tbs.dbf'
SIZE 25M REUSE AUTOEXTEND ON MAXSIZE 256M;

CREATE USER strmadmin IDENTIFIED BY xxx
DEFAULT TABLESPACE streams_tbs
QUOTA UNLIMITED ON streams_tbs;

grant dba to strmadmin;

BEGIN
DBMS_STREAMS_ADM.SET_UP_QUEUE(
queue_table => 'strmadmin.streams_queue_table',
queue_name => 'strmadmin.streams_queue');
END;
/

Vous pouvez créer des fichiers de Standby redo log si vous voulez effectuer une capture « Real Time ». Pour cela, vérifiez la taille des fichiers Redo Logs sur la base primaire :

select THREAD#, GROUP#, BYTES
from v$log
order by thread#, group#;

THREAD# GROUP# BYTES
------- ---------- ----------
1 1 52428800
1 2 52428800
1 3 52428800

Une fois que vous avez la taille des redo logs de la base primaire, vous pouvez créer des standby redo logs de la même taille sur la base clone :

alter database add standby logfile thread 1 group 21
('/oradata/WHITE/redo21_1.log') SIZE 52428800;
alter database add standby logfile thread 1 group 22
('/oradata/WHITE/redo22_1.log') SIZE 52428800;
alter database add standby logfile thread 1 group 23
('/oradata/WHITE/redo23_1.log') SIZE 52428800;

L’étape suivante consiste à créer le processus de capture et à ajouter une règle pour inclure tous les objets du schéma DEMO doivent être capturés.

BEGIN
DBMS_CAPTURE_ADM.CREATE_CAPTURE(
queue_name => 'strmadmin.streams_queue',
capture_name => 'streams_capture',
rule_set_name => NULL,
source_database => 'BLACK',
use_database_link => false,
first_scn => 497637,
logfile_assignment => 'implicit');
END;
/

BEGIN
DBMS_CAPTURE_ADM.SET_PARAMETER(
capture_name => 'streams_capture',
parameter => 'downstream_real_time_mine',
value => 'Y');
END;
/

BEGIN
DBMS_STREAMS_ADM.ADD_SCHEMA_RULES(
schema_name => 'demo',
streams_type => 'capture',
streams_name => 'streams_capture',
queue_name => 'strmadmin.streams_queue',
include_dml => true,
include_ddl => true,
include_tagged_lcr => true,
source_database => 'BLACK',
inclusion_rule => true);
END;
/

Plusieurs objets du schéma demo de notre exemple ne peuvent pas etre capturés. On supposera qu’ils ne sont pas important ou pas modifiés dans cet exemple et il suffit de les exclure de la capture en ajoutant une règle négative :

-- Objets non supportés par Streams
select table_name
from dba_streams_unsupported
where owner='DEMO'
and auto_filtered='NO';

-- Vues matérialisées
select container_name
from dba_mviews
where owner='DEMO';

-- Objets sans UK/PK
select table_name
from dba_tables t
where owner='DEMO'
and table_name not in
(select table_name
from dba_constraints
where owner= t.owner
and constraint_type in ('P','U'));

-- Exclusion des tables non supportées par Streams
set serveroutput on
BEGIN
for i in 2..3 loop
dbms_output.put_line('T_DEMO'||to_char(i));
DBMS_STREAMS_ADM.ADD_TABLE_RULES(
table_name => 'demo.t_demo'||to_char(i),
streams_type => 'capture',
streams_name => 'streams_capture',
queue_name => 'strmadmin.streams_queue',
include_dml => true,
include_ddl => true,
source_database => 'BLACK',
inclusion_rule => false);
end loop;
END;
/

La capture est paramétrée, vous pouvez ajouter le process d’apply
ainsi que les règles d’inclusion et d’exclusion associées :

-- Create the Apply and add the DEMO schema
BEGIN
DBMS_STREAMS_ADM.ADD_SCHEMA_RULES(
schema_name => 'demo',
streams_type => 'apply',
streams_name => 'streams_apply',
queue_name => 'strmadmin.streams_queue',
include_dml => true,
include_ddl => true,
include_tagged_lcr => true,
source_database => 'BLACK',
inclusion_rule => true);
END;
/

-- Exclusion des tables non supportées par Streams
set serveroutput on
BEGIN
for i in 2..3 loop
dbms_output.put_line('T_DEMO'||to_char(i));
DBMS_STREAMS_ADM.ADD_TABLE_RULES(
table_name => 'demo.t_demo'||to_char(i),
streams_type => 'apply',
streams_name => 'streams_apply',
queue_name => 'strmadmin.streams_queue',
include_dml => true,
include_ddl => true,
source_database => 'BLACK',
inclusion_rule => false);
end loop;
END;
/

Enfin, il faut definir pour les objets cibles, le SCN qui correspond à leur instianciation à partir de la source, c’est à dire le SCN dans la colonne next_change# du dernier fichier archivelog appliqué lors de la commande RMAN DUPLICATE.

BEGIN
DBMS_APPLY_ADM.SET_SCHEMA_INSTANTIATION_SCN(
SOURCE_SCHEMA_NAME => 'demo',
SOURCE_DATABASE_NAME => 'BLACK',
INSTANTIATION_SCN => 498370);
END;
/

select *
from DBA_APPLY_INSTANTIATED_SCHEMAS;

BEGIN
for i in (select table_name
from dba_tables
where owner='DEMO')
loop
DBMS_APPLY_ADM.SET_TABLE_INSTANTIATION_SCN(
SOURCE_OBJECT_NAME=>'demo.'||i.table_name,
SOURCE_DATABASE_NAME=>'BLACK',
INSTANTIATION_SCN=>498370);
end loop;
end;
/

select *
from DBA_APPLY_INSTANTIATED_OBJECTS;

8. Démarrer et superviser Streams

Utilisez dbms_capture_adm et dbms_apply_adm pour démarrer les process de capture et d’apply comme ci-dessous :

exec dbms_capture_adm.start_capture('streams_capture');

exec dbms_apply_adm.start_apply('streams_apply');

set lines 150
col capture_name format a16
col state format a70
col total_messages_captured format 999999999999
col apply_name format a16
select capture_name,
state,
total_messages_captured,
apply_name
from v$streams_capture;

9. Transférez et enregistrer les archivelogs de la base primaire à la base clone

Vous pouvez utiliser les capacités de transport des bases de données Enterprise Edition en parametrant la base de données primaire comme ci-dessous :

alter system
set log_archive_dest_2=
'service=WHITE lgwr async noregister valid_for=(online_logfiles,all_roles) db_unique_name=WHITE';

alter system set log_archive_dest_state_2=defer;

alter system switch logfile;

alter system set log_archive_dest_state_2=enable;

alter system switch logfile;

Supervisez ensuite la base clone et en particulier, vérifiez les fichiers alert.log, les process RFS, les process de capture et d’apply, les archivelogs enregistrés pour Streams et les Standby Redo logs :

select process, status, client_process, thread#, sequence#, blocks
from v$managed_standby
where process = 'RFS';

select capture_name,
state,
total_messages_captured,
apply_name
from v$streams_capture;

select apply_name, status, error_number, error_message
from dba_apply;

select consumer_name, source_database, thread#, sequence#
from dba_registered_archived_log
order by consumer_name, thread#, sequence#;

select thread#, group#, dbid, sequence#, bytes, status
from v$standby_log
order by thread#, group#;

10. Tester la configuration

Outre le fait que la configuration n’ait pas d’erreur, vérifiez que les données modifiées sont bien propagées sur la base clone. Pour cela, modifiez des données sur la source. Vous pouvez également créer une table comme ci-dessous sur la base primaire :

create table demo.streams_healthcheck(
id number primary key,
last_update date);

insert into demo.streams_healthcheck (id, last_update)
values (1,sysdate);

commit;

alter session set nls_date_format='DD/MM/YYYY HH24:MI:SS';

select last_update
from demo.streams_healthcheck
where id=1;

11. Basculer la base clone en production

Déjà la fin de l’histoire :

  1. Arrêtez votre application
  2. Mettez à jour une données dans une table test et validez qu’elle est appliquée dans la base production
  3. Effectuez les derniers changements nécessaires dans la base clone (Activez les triggers, changez les séquences,…)
  4. Démarrez l’application sur votre base clone en 11g qui est maintenant la base de production.

C’est finalement assez simple et si vous avez un script fiable pour la dernière étape, l’indisponibilité peut être de l’ordre de quelques minutes ou même moins. En outre Streams vous permet aussi, pourquoi pas, de transformer le schéma sur la base clone pour permettre la mise à jour d’une application dans la foulée. Vous pouvez aussi utiliser une réplication bi-directionnelle et ainsi disposer d’un plan de retour arrière même si vous decouvrer un problème après plusieurs heures.

Bonne chance pour votre projet et si vous voulez un coup de main, pensez à nous !

5 réflexions sur “Migrer de 10g en 11g avec Zéro Indisponibilité et Streams”

  1. Ca dépend. Il se peut dans certains cas qu’il soit impossible de mettre à jour 1 système à la fois… Ce n’est pas souhaitable évidemment et dans tous les SI, les bases de données sont interconnectées, ce qui ne veut pas dire qu’on doive aligner toutes les modifications.

    Tout ça est question d’architecture, de risque et de gestion du changement au fond.

  2. Bjr,
    Lorsque l’on dispose d’une vingtaine de bases en prod, échangeant les unes avec les autres et que l’on doit les migrer vers une version Oracle supérieure, toutes les bases doivent-elles être migrées en même temps ou peut-on les migrer une à une sur plusieurs jours alors qu’elles interagissent les unes avec les autres. En d’autres termes, le changement de version influence t-il la définition de certaines données de la base au point de perturber des bases non dans la même version ?
    Merci d’avance.
    Siouat

  3. Bonjour,

    Cette technique est elle envisageable pour migrer de 9i vers 10g ?

  4. Bonjour Grégory,

    Merci et bravo pour cette démonstration très claire et didactique !

    Wilfrid

    Bon retour en France pour dans quelques jours. Il fait beau en ce moment 😉

Les commentaires sont fermés.