Blog

Blog

Migration 10g->11G avec changement d'OS User

Le client pour lequel j'ai du faire cette migration a changé de stratégie pour la gestion des users OS et j'ai donc du faire une migration de 10gR2 vers 11gR2 sans dbua... Hé oui, c'est la surprise du chef, il n'est pas possible d'utiliser dbua dans ce cas... (cf note metalink 984511.1). D'accord, le plus dur dans un upgrade, c'est de convaincre son chef que c'est important, utile et que ça ne lui coûtera pas trop cher, mais une fois que c'est fait, on n'est pas arrivés pour autant! Voici donc la liste des étapes à effectuer (je passerai rapidement sur certaines choses triviales) :

Installer les binaires 11gR2

Je vous laisse faire cette action tout seul. C'est pas le plus compliqué (quoi que l'install des prérequis quand on n'a pas yum n'est pas ce que je préfère), mais la documentation Oracle est très complète sur ce point.

Lancer l'outil de pré-upgrade

Là encore, c'est assez simple, connecté en sqlplus as sysdba sur l'ancien user oracle, lancer les requêtes suivantes :
SPOOL log_file.log
@new_oracle_home/rdbms/admin/utlu112i.sql
SPOOL OFF

Vérifier que les vues matérialisées sont à jour

Requête toute simple : s'il n'y a pas de résultat, il n'y a rien à faire. Sinon il faudra rafraîchir les vues sorties :
SELECT * FROM sys.obj$ o, sys.user$ u, sys.sum$ s WHERE o.type# = 42 AND bitand(s.mflags, 8) = 8;

S'assurer qu'aucun fichier n'a besoin d'un recovery

Si la requête suivante ramène des résultats, lancer le recovery jusqu'à ce qu'elle n'en ramène plus.
SELECT * FROM v$recover_file;

Vérifier qu'aucun fichier n'est en mode Backup

Remettre les fichiers résultats de cette requête ne mode normal :
SELECT * FROM v$backup WHERE status != 'NOT ACTIVE';

Résoudre les transactions en attente

Si la première requête ramène des résultats, lancer la procedure dbms_transaction.purge_lost_db_entry('id_trans') et commiter.
SELECT * FROM dba_2pc_pending;

Purger la corbeille

Il suffit de faire la commande suivante sous sqlplus :
PURGE dba_recyclebin;

Faire une sauvegarde (;-))

Je préfère faire une sauvegarde à froid pour ce genre de chose, mais il est parfaitement possible de faire une sauvegarde à chaud si on est en mode archive log.
rman target / nocatalog
RUN
{ 
    ALLOCATE CHANNEL c1 TYPE DISK; 
    SHUTDOWN IMMEDIATE; 
    STARTUP MOUNT; 
    BACKUP DATABASE FORMAT '/u01/app/backup_before_upgrade/%U' TAG before_upgrade; 
    BACKUP CURRENT CONTROLFILE FORMAT '/u01/app/backup_before_upgrade/control01.ctl'; 
}

Préparation du nouveau pfile

On commence par se connecter à la base pour créer le pfile.
CREATE PFILE='your_path_and_file_name' FROM SPFILE;
Puis, on regarde le pfile et on supprime les paramètres dépréciés (background_dump_dest, user_dump_dest) et on modifie également les paramètres permettant de bénéficier des nouvelles fonctionnalités (memory_max_target, memory_target vs sga_target, sga_max_target, pga_aggregate_target). On nn'oublie pas de mettre le paramètre de compatibilité (*.compatible='10.2.0.3.0').

Modifier /etc/oratab

Il faut penser à modifier le fichier /etc/oratab pour faire pointer le ORACLE_HOME de la base à migrer vers notre tout nouveau ORACLE_HOME 11G.

Modifier les droits sur les fichiers de la base

On commence par arrêter la base avant de manipuler les droits OS sur les fichiers.
shutdown immediate
Il faut ensuite mettre comme owner des répertoires de la base le nouveau user Oracle. Voici la liste des répertoires à modifier :
  • Répertoires des fichiers de données
  • Répertoires des fichiers de contrôles
  • Répertoires des pfile et spfile
  • Répertoires des archivelogs
  • Répertoires des redos
Commande système à passer sous Linux:
chown -R newUser:oinstall your_rep

Lancer l'outil d'upgrade

Avant de lancer l'outil, bien s'assurer que les variables suivantes sont settées correctement et si nécessaire sur l'environnement 11G :
  • ORACLE_HOME
  • PATH
  • ORACLE_SID
Aller dans le répertoire $ORACLE_HOME/rdbms/admin et démarrer l'instance via sqlplus (connecté as sysdba):
STARTUP MIGRATE PFILE='your_pfile_destination'
Lancer l'outil d'upgrade en mettant un fichier de spool :
SPOOL 'your_log_file'
@catupgrd.sql
SPOOL OFF
Cette exécution est TRES longue... L'outil arrête lorsqu'il a finit et vous déconnecte d'sqlplus. Il faut ensuite redémarrer la base et lancer les outils de post-upgrade :
STARTUP PFILE='your_pfile'
@utlu112s.sql
-> Tous les éléments doivent être valides Puis, on peut lancer la dernière phase de l'upgrade :
@catuppst.sql
Et on recompile les objets invalides :
@utlrp.sql
Et on peut regarder les objets invalides :
SELECT count(*) FROM dba_invalid_objects;
Si tout est bon, on peut créer le spfile :
CREATE SPFILE='your_spfile' FROM PFILE='your_pfile';
Il ne reste plus qu'à modifier les différents scripts qui tournent sur la base pour pointer sur le bon ORACLE_HOME et à s'assurer que les connecteurs JDBC ou autres sont compatibles Oracle 11gR2!

Statspack ou l'ancêtre d'AWR

Hé oui, statspack a été remplacé par AWR (Automatic Workload Repository), mais cette fonctionnalité fait partie du diagnostic pack qui est une option même pour la version Enterprise. (Il faut d'ailleurs faire un peu attention, car cette fonctionnalité est installée même si elle n'a pas été payée...)
Pour ne pas payer ou risquer d'être en infraction, il reste le statspack. je n'ai aps trouvé de vraie doc sur ce statspck en 11g, mais sachez que la doc 9i est toujours d'actualité.

Installer Statspack

Hé oui, contrairement à AWR, il faut l'installer avant de l'utiliser. Autant vous dire que vous ne pourrez jamais sortir un rapport sur ce qu'il s'est passé hier sur la base de production...
Pour installer statspack, il suffit de lancer le script spcreate.sql qui se trouve sous $ORACLE_HOME/rdbms/admin.
sqlplus / as sysdba @?/rdbms/admin/spcreate
Le script devrait vous demander certaines informations comme le mot de passe du user perfstat, le tablespace d'espace temporaire et le tablespace par défaut du user perfstat.

Et c'est tout!

Faire un snapshot

Pour faire un snapshot, il suffit de lancer le script suivant (après s'être connecté avec le user perfstat) :
EXECUTE statspack.snap;

Faire un rapport

Pour faire un rapport, il faut lancer le script suivant (toujours connecté avec le user perfstat) :
@?/rdbms/admin/spreport
Ensuite, on peut analyser plus avant un ordre SQL en fournissant le hash value de la requête SQL au script suivant
@?/rdbms/admin/sprepsql

Programmer des snapshots réguliers

Comme on ne prévient pas quand on va en avoir besoin, il vaut mieux passer régulièrement des snapshots. Pour ça, on peut soit créer un script qui génère les snapshots et le lancer via un scheduler quelconque, soit utiliser Oracle pour les lancer. je préfère utiliser Oracle :-). En 9i, on utilisera DBMS_JOB, à partir de la 10g, on préfèrera DBMS_SCHEDULER.

Modifier les fenêtres de gather stat job en 11g

Je m'occupe d'une certaine plateforme de vote électronique. Comme chacun sait, un vote électronique est disponible H24 7j/7. Il a donc fallu que je me penche sur le recalcul automatique des stats Oracle pour m'assurer qu'ils n'allaient pas intervenir à un moment important du scrutin.

Oracle autotasks

En 11g, les tâches de maintenance Oracle sont lancées dans un scheduler. Ces tâches sont nommées "autotask" (le terme est important car sans lui, il est difficile de s'y retrouver dans la doc Oracle). Ces jobs de maintenance se lancent suivant des fenêtres de maintenance.

Gather stats

Le gather_stats_jobs d'Oracle 10g a été remplacé par une "tâche" que vous pouvez retrouver dans la vue dba_autotask_client. Pour savoir quand il se lance, il vous reste la requête suivante :
SELECT client_name, window_group
FROM dba_autotask_client
WHERE client_name='auto optimizer stats collection';
Vous récupérez donc un "window_group" (normalement, ça devrait s'appeler ORA$AT_WGRP_OS), vous pouvez requéter la table dba_scheduler_window_groups mais elle ne vous apprendra pas grand chose sur les heures de lancement de votre job En fait un "groupe de fenêtres" comporte différentes fenêtres (sic!). Bref, il faut requêter chaque fene^tre ensuite pour savoir quand votre job va se lancer :
SELECT window_group_name, window_name
FROM dba_scheduler_wingroup_members
WHERE window_group_name='ORA$AT_WGRP_OS';
Et voilà votre liste de fenêtres :

WINDOW_GROUP_NAME WINDOW_NAME
------------------------------ ------------------------------
ORA$AT_WGRP_OS MONDAY_WINDOW
ORA$AT_WGRP_OS TUESDAY_WINDOW
ORA$AT_WGRP_OS WEDNESDAY_WINDOW
ORA$AT_WGRP_OS THURSDAY_WINDOW
ORA$AT_WGRP_OS FRIDAY_WINDOW
ORA$AT_WGRP_OS SATURDAY_WINDOW
ORA$AT_WGRP_OS SUNDAY_WINDOW

Visualisation de mes fenêtres

A ce moment-là, vous devrez avoir une petite idée de quelle fenêtre vous voulez modifier. Si vous avez un doute, vous pouvez toujours demander le détail en requêtant dba_scheduler_windows :
SELECT window_name, repeat_interval, duration
FROM dba_scheduler_windows 
WHERE window_name IN ('SATURDAY_WINDOW','SUNDAY_WINDOW');

WINDOW_NAME
------------------------------
REPEAT_INTERVAL
--------------------------------------------------------------------------------
DURATION
---------------------------------------------------------------------------
SATURDAY_WINDOW
freq=daily;byday=SAT;byhour=6;byminute=0; bysecond=0
+000 20:00:00

SUNDAY_WINDOW
freq=daily;byday=SUN;byhour=6;byminute=0; bysecond=0
+000 20:00:00
On voit donc que la maintenance se lance à 6h00 et dure 20h00 les week-ends. Je vais donc pouvoir modifier mes fenêtres du samedi et du dimanche pour que la fenêtre de maintenance commence à 22h00 et dure 4h00. (C'est par défaut le comportant des jours ouvrés.)

Modification de mes fenêtres

Je fais donc ceci (et la même chose pour SUNDAY) :
BEGIN
  dbms_scheduler.disable(
    name  => 'SATURDAY_WINDOW');
  dbms_scheduler.set_attribute(
    name      => 'SATURDAY_WINDOW',
    attribute => 'DURATION',
    value     => numtodsinterval(4, 'hour'));
  dbms_scheduler.set_attribute(
    name      => 'SATURDAY_WINDOW',
    attribute => 'REPEAT_INTERVAL',
    value     => 'freq=daily;byday=SAT;byhour=22;byminute=0; bysecond=0');
  dbms_scheduler.enable(
    name => 'SATURDAY_WINDOW');
END;
/
Et voilà!

Pour aller un peu plus loin avec les stats

Il faut savoir que ce n'est pas parce que ces fenêtres de maintenance sont correctement settées que les statistiques seront forcément recalculées. En effet, Oracle est intelligent et ne recalcule les stats sur une table que s'il considère que c'est nécessaire (plus de 20% de modification sur la table par défaut).

Duplicate : RMAN-06217 et RMAN-04006

Vous avez une nouvelle machine qui doit héberger une standby et vous voulez qu'rman s'en occupe tout seul comme il sait si bien le faire. Vous avez donc créé un pfile minimal et avez démarré l'instance. la base est en nomount. (Ben oui, elle peut pas aller plus loin, y a pas de control file.) Les fichiers tnsnames et de mots de passe sont OK. Le listener tourne, bref tout marche, sauf que non!

Arrivée de la RMAN-06217

Vous tentez donc une connexion rman comme ça : (Dans mes exemples la primaire s'appelle rachel et la standby trenton)
rman target sys/xxx@rachel auxiliary /
et ça semble marcher :
Recovery Manager: Release 11.2.0.1.0 - Production on Mon Oct 3 10:25:57 2011
Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.
connected to target database: RACHEL (DBID=YYY)
connected to auxiliary database: TRENTON (not mounted)
RMAN
puis, bous lancez votre duplicate et arrive l'erreur fatidique (sans avoir été invitée):

run 
{
allocate auxiliary channel t1 type disk;
allocate channel t2 type disk;
duplicate target database for standby from active database password file spfile;
}

using target database control file instead of recovery catalog
allocated channel: t1
channel t1: SID=95 device type=DISK

allocated channel: t2
channel t2: SID=13 device type=DISK

Starting Duplicate Db at 03-OCT-11
released channel: t1
released channel: t2
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 10/03/2011 10:26:15
RMAN-06217: not connected to auxiliary database with a net service name

Arrivée de la RMAN-04006

Vous remplissez donc consciencieusement votre tnsnames.ora. Vous faites même un petit tnsping pour être sûr :
tnsping trenton

TNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 03-OCT-2011 10:36:03

Copyright (c) 1997, 2009, Oracle.  All rights reserved.

Used parameter files:
/admin/oracle/network/admin/sqlnet.ora

Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = yyy)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = TRENTON)))
OK (0 msec)
Fiers de vous, vous lancez donc la commande qui doit marcher :
rman target sys/xxx@rachel auxiliary sys/xxx@trenton

Recovery Manager: Release 11.2.0.1.0 - Production on Mon Oct 3 10:27:25 2011

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

connected to target database: RACHEL (DBID=YYY)
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-00554: initialization of internal recovery manager package failed
RMAN-04006: error from auxiliary database: ORA-12528: TNS:listener: all appropriate instances are blocking new connections
Et le pire, c'est que c'est logique! La BDD secondaire n'a pas pu s'enregistrer auprès du listener (ben oui, elle est toujours à l'état nomount), on ne peut donc pas se connecter en utilisant un service name!

La solution

D'après la note metalink Connection to Auxilary using connect string failed with ORA-12528 (Doc ID 419440.1) (Attention, authentification nécessaire), il faut modifier le tnsnames comme ça :

TRENTON =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = yyy)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = TRENTON)
      (UR=A)
    )
  )

Délocker un user

Ben oui, ça arrive... Le client a tapé plusieurs fois un mauvais mot de passe et le compte est locké...

Comportement du lock

Tout est paramétrable lorsque vous créez le profile. Si vous n'avez rien spécifié, le compte sera locké pendant une journée au bout de 10 tentatives de login avec un faux mot de passe.
Pour retrouver ces infos :
SELECT resource_name, limit
FROM dba_profiles INNER JOIN dba_users ON dba_profiles.profile=dba_users.profile
WHERE username='user_name' 
  AND  resource_name IN ('PASSWORD_LOCK_TIME','FAILED_LOGIN_ATTEMPTS');

Voir le statut

Voilà la requête :
SELECT account_status 
FROM dba_users 
WHERE username='user_name'; 
Si le résultat est OPEN, votre compte n'est pas locké, si vous trouvé LOCKED(TIME), c'est locké parce le user s'est trompé trop de fois de mot de passe.

Délocker

C'est tout simple :
ALTER USER user ACCOUNT UNLOCK;
J'espère que vous n'aurez pas à vous en servir trop souvent!

Les commandes Rman

Je ne sais pas de quelle planète vient le gars qui a inventé les commandes rman, mais c'est pas la même que la mienne! Je galère pour retrouver la syntaxe des commandes à chaque fois.

En passant, c'est pas génial qu'Oracle n'ai pas prévu une certaine cohérence entre ses outils, mais bon, peut-être en 13c ?

Bref, voilà le lien vers la doc Oracle 11g pour les commandes rman : http://download.oracle.com/docs/cd/E11882_01/backup.112/e10643/rcmcomma005.htm

Mettre en place un observer

Bon, admettons que vous ayiez un dataguard qui fonctionne bien. C'est bien. Mais si ça plante à 2h00 du matin, faut vous appeler pour faire la bascule ? Hé non, y a observer pour ça, mais faut le configurer d'abord...

Standby OK

Commencez par vérifier que votre standby applique bien les redos de la primaire... (cf le billet Vérifier qu'une standby applique bien les logs)

LogXptMode

Il faut que la propriété LogXptMode soit settée à SYNC pour chaque base de données. La requête se lance sous dgmgrl.
SHOW DATABASE 'DB' 'LogXptMode';
Si la propriété n'est pas à SYNC, mettez-la à cette valeur :
EDIT DATABASE 'DB' SET PROPERTY 'LogXptMode'='SYNC';
Atention cette manipulation est à faire sur le maître et sur l'esclave!

FastStartFailoverTarget

Oui, c'est une succession de variable à setter... Je suis désolée. Cette option n'est à modifier que s'il y a ambiguïté sur la standby sur laquelle basculer.
Pour connaître la valeur de FastStartFailoverTarget, il faut faire ça:
SHOW DATABASE 'DB' 'FastStartFailoverTarget';
Pour modifier la valeur (sur la primaire uniquement) :
EDIT DATABASE 'DB' SET PROPERTY FastStartFailoverTarget='your_value';

Mode de protection

Il est temps de passer de 'Maximum performance' à 'Maximum availability'.
EDIT CONFIGURATION SET PROTECTION MODE AS MAXAVAILABILITY;

Flashback

C'est le moment d'activer le flashback sur vos bases. Pour savoir si c'est déjà fait (sous sqlplus sur chacune des bases) :
SELECT flashback_on FROM v$database;
Profitez-en pour vérifier aussi les valeurs des paramètres suivantes:
SHOW PARAMETER undo_retention;
SHOW PARAMETER undo_management;
SHOW PARAMETER db_flashback_retention_target;
SHOW PARAMETER db_recovery_file_dest_size;
SHOW PARAMETER db_recovery_file_dest;
Si c'est pas fait, utilisez les ordres ci-dessous (pour le maître):
ALTER SYSTEM SET undo_retention=3600 SCOPE=SPFILE;
ALTER SYSTEM SET undo_management='AUTO' SCOPE=SPFILE;
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
SHOW PARAMETER undo;
ALTER SYSTEM SET db_flashback_retention_target=4320 SCOPE=BOTH;
ALTER DATABASE ARCHIVELOG;
ALTER SYSTEM SET db_recovery_file_dest_size=size;
ALTER SYSTEM SET db_recovery_file_dest=directory-specification;
ALTER DATABASE FLASHBACK ON;
ALTER DATABASE OPEN;
Pour l'esclave, il faut arrêter l'apply (puisque la base est déjà montée) via dgmgrl comme ça :
EDIT DATABASE db SET STATE='APPLY-OFF';
Puis on le remet en place :
EDIT DATABASE db SET STATE='APPLY-ON';

Démarrer l'observer

Il vaut mieux spécifier un fichier de log:
dgmgrl -logfile Your_logfile
Pour démarrer l'observer, il faut faire
start observer;
Dgmgrl ne vous rendra pas la main. Pour cette raison, il est préférable d'encapsuler tout ça dans un script lancé ensuite avec un nohup.

Activer le Fast-start failover

C'est ce qui permet de basculer rapidement, comme son nom l'indique. Il faut faire cette commande sous dgmgrl.
ENABLE FAST_START FAILOVER;
Vous pouvez vérifier que tout va bien en faisant ça sous dgmgrl:
SHOW FAST_START FAILOVER;
Ou ça sous sqlplus (sur chacune des bases) :
SELECT fs_failover_status, fs_failover_current_target, fs_failover_threshold, fs_failover_observer_present,fs_failover_observer_host FROM v$database;

Pour en savoir plus

la doc Oracle dispo ici : http://download.oracle.com/docs/cd/E11882_01/server.112/e17023/cli.htm#DGBKR3430

NID-00135 et ORA-01092

Oui, je sais on ne doit pas utiliser nid dans la vie normale d'une base de données, mais bon si l'outil existe c'est pour qu'on l'utilise, non ?

De plus, C'était vraiment nécessaire. Je ne vais pas détailler ici le pourquoi du comment, mais j'avais besoin de cloner une base à partir d'une standby et le rman duplicate ne fonctionne pas à partir d'une standby... J'expliquerai dans un prochain poste comment nous nous en sommes sorti (un projet de cette ampleur, ça ne peut être qu'un travail d'équipe!!)

A la pêche aux infos

Dans la doc Oracle, je trouve ça :
NID-00135: There are number active threads
Cause: is that the database crashed the last time it was shut down.
Action: Ensure that all threads are closed before retrying the operation. Start and open the database to perform crash recovery, then shut down with the NORMAL or IMMEDIATE options to close it cleanly. Finally, try running the utility again.

C'est parti!

Bon, ben allons-y gaiement, ma base était bien dans l'état mount, je tente donc ça :
SHUTDOWN IMMEDIATE.
STARTUP;
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
Et ce petit coquin d'Oracle me sort :
startup

ERROR at line 1:
ORA-01092: ORACLE instance terminated. Disconnection forced
Là, ça sent pas bon du tout...

Re-pêche aux infos

Cette fois-ci, la doc Oracle ne me rassure pas trop :
ORA-01092: ORACLE instance terminated. Disconnection forced
Cause: The instance this process was connected to was terminated abnormally, probably via a shutdown abort. This process was forced to disconnect from the instance.
Action: Examine the alert log for more details. When the instance has been restarted, retry action.
Et c'est pire dans l'alert log :
ORA-00704: bootstrap process failure
ORA-39700: database must be opened with UPGRADE option
Pfff, on dirait que j'ai mis un sacré b***el sur ma base! Que dit la doc ?
ORA-39700: database must be opened with UPGRADE option
Cause: A normal database open was attempted, but the database has not been upgraded to the current server version.
Action: Use the UPGRADE option when opening the database to run catupgrd.sql (for database upgrade), or to run catalog.sql and catproc.sql (after initial database creation).

Résolution ?

Bon, ben c'est reparti :
SHUTDOWN IMMEDIATE.
STARTUP UPGRADE;
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ouf c'est passé!

Un petit nid ?

Cette fois-ci nid s'est rédoulé sans problème... Nid me prévient gentiment de ce qu'il me reste à faire
Database name changed to ISI0RCT.
Modify parameter file and generate a new password file before restarting.
Database ID for database ISI0RCT changed to 3511798175.
All previous backups and archived redo logs for this database are unusable.
Database is not aware of previous backups and archived logs in Recovery Area.
Database has been shutdown, open database with RESETLOGS option.
Succesfully changed database name and ID.
DBNEWID - Completed succesfully.

Et on repart!

Après modification du dbname dans le pfile, j'ai pu redémarrer ma base correctement!

ASM a perdu ses disques après un reboot

J'ai rencontré ce problème sur du Red Hat 5.3, 5.4 et 5.5.

Symptômes

Je reboote la machine et oracleasm ne voit plus aucun disque! Mes disques sont des lv. Je n'ai pas rencontré ce problème avec des LUN.

Correction

C'est tout bête : il suffit de mettre en commentaire la ligne KERNEL=="dm-[0-9]*", ACTION=="add", OPTIONS+="ignore_device du fichier /etc/udev/rules.d/90-dm.rules.

C'est vraiment bête mais si on ne fait pas ça, ASM ne cherche ses disques que sous /dev/dm*.

Pour aller plus loin

Vous pouvez consulter cette page de blog : http://www.myoraclesupports.com/content/elrh-5-oracleasm-listdisks-not-showing-devices-after-reboot-or-restart
Ou le bug 6736803 https://support.oracle.com/CSP/main/article?cmd=show&type=BUG&id=6736803

Surveiller l'activité d'une base

Un collègue vient de me passer une requête magique pour suivre un peu mieux l'activité d'une base en fonction du nombre de switch d'archives log. (Bien sûr si votre base n'est pas en mode archive log, c'est pas la peine.)
set lines 120;
set pages 999;
SELECT * FROM (
SELECT * FROM (
SELECT TO_CHAR(FIRST_TIME, 'DD/MM') AS "DAY" 
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '00', 1, 0)), '99') "00" 
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '01', 1, 0)), '99') "01"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '02', 1, 0)), '99') "02"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '03', 1, 0)), '99') "03"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '04', 1, 0)), '99') "04"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '05', 1, 0)), '99') "05"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '06', 1, 0)), '99') "06"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '07', 1, 0)), '99') "07"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '08', 1, 0)), '99') "08"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '09', 1, 0)), '99') "09"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '10', 1, 0)), '99') "10"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '11', 1, 0)), '99') "11"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '12', 1, 0)), '99') "12"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '13', 1, 0)), '99') "13"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '14', 1, 0)), '99') "14"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '15', 1, 0)), '99') "15"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '16', 1, 0)), '99') "16"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '17', 1, 0)), '99') "17"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '18', 1, 0)), '99') "18"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '19', 1, 0)), '99') "19"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '20', 1, 0)), '99') "20"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '21', 1, 0)), '99') "21"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '22', 1, 0)), '99') "22"
, TO_CHAR(SUM(DECODE(TO_CHAR(FIRST_TIME, 'HH24'), '23', 1, 0)), '99') "23"
FROM V$LOG_HISTORY
WHERE extract(year FROM FIRST_TIME) = extract(year FROM sysdate) 
GROUP BY TO_CHAR(FIRST_TIME, 'DD/MM')
) ORDER BY TO_DATE(extract(year FROM sysdate) || DAY, 'YYYY DD/MM') DESC
) WHERE ROWNUM < 8 
Oui, ça fait peur, mais c'est très efficace!

Espace occupé par une base de données Oracle

je compile ici deux requêtes très intéressante pour surveiller l'espace occupé par une base de données Oracle.

ASM

Si vous êtes sous ASM, vous pouvez surveiller l'espace occupé sur chaque diskgroup (Les données étant load balancées, je ne vois pas trop l'intérêt de travailler sur l'espace occupé dans chaque disque).
SET LINES 200
SET PAGES 100
COL dg_name FORMAT A20
COL d_name FORMAT A20
COL action FORMAT A20
COL etat FORMAT A20
COL path FORMAT A30
COL size_mb FORMAT 9999
COL free_mb FORMAT 9999

SELECT NVL(adg.name,'n/a') dg_name,
ROUND(SUM(ad.TOTAL_MB/1024)) SIZE_GB,
ROUND(SUM(ad.FREE_MB/1024)) FREE_GB,
ROUND(SUM(ad.TOTAL_MB/1024))-ROUND(SUM(ad.FREE_MB/1024)) USED_GB
FROM v$asm_diskgroup adg LEFT JOIN v$asm_disk ad ON ad.group_number = adg.group_number
WHERE ad.header_status <> 'foreign'
GROUP BY NVL(adg.name,'n/a')

Tablespaces

Si vous n'êtes pas sous ASM, l'espace pris sur le disque n'est pas un bon indicateur étant donné que l'espace alloué dans les tablespaces est considéré par l'OS comme occupé alors qu'Oracle peut très bien ne pas l'utiliser...

J'ai récupéré cette requête auprès d'une collègue qui elle même la tenait d'un collègue etc... Bref, je ne connais pas l'auteur de cette requête s'il se reconnaît et désire que je la supprime, il lui suffit de me contacter...
set pagesize 1000
set linesize 150

select  SUBSTR(ORA_DATABASE_NAME,0,32) as DATABASE_NAME,
SUBSTR(HOST_NAME,0,32) as HOST_NAME,
USER,
to_char(SYSDATE,'YYYY-MM-DD hh24 :mi') as CUR_DATE
from v$instance ;


set echo off ;



col STATUT       for A20        head "STATUT"             wrap
col tbsName      for A25        head "TABLESPACE"         wrap
col prmType      for A1         head "T"                  trunc
col extMan       for A1         head "X"                  trunc
col tbsStat      for A2         head "On"                 trunc
col tbsMsize     for 999,990    head "TAILLE|Allouée|(M)" trunc
col tbsMsizeMax  for 999,990    head "TAILLE|Limite|(M)"  trunc
col usedSize     for 999,990    head "VOLUME|Utilise|(M)" trunc
col usedPct      for 999.99     head "VOLUME|/alloué|(%)" trunc
col usedTotPct   for 999.99     head "VOLUME|/limite|(%)" trunc

select
--decode( greatest(90,nvl(t.BYTES/a.BYTES*100,0)), 90, 'TAILLE_TBS_OK', 'WARNING_TBS_FULL' ) STATUT,
case
when d.TABLESPACE_NAME like 'SYS%'                            then '   NO_CHECK'
when 
case
when nvl(w.BYTES /1024/1024, 0) != 0 then nvl((a.BYTES - NVL(f.BYTES, 0)) / nvl(w.BYTES, 0) * 100, 0)
else nvl((a.BYTES - NVL(f.BYTES, 0)) / a.BYTES * 100, 0)
end > 95
then '!! ALERT_TBS   > 95%'
when 
case
when nvl(w.BYTES /1024/1024, 0) != 0 then nvl((a.BYTES - NVL(f.BYTES, 0)) / nvl(w.BYTES, 0) * 100, 0)
else nvl((a.BYTES - NVL(f.BYTES, 0)) / a.BYTES * 100, 0)
end > 85
then '!  WARNING_TBS > 85%'
else '   TBS_SIZE_OK'
end STATUT,
d.TABLESPACE_NAME                                   tbsName,
decode (d.CONTENTS, 'PERMANENT', 'P',
'TEMPORARY', 'T',
'UNDO', 'U')               prmType,
decode (d.EXTENT_MANAGEMENT, 'LOCAL', 'L', 'D')     extMan,
decode (d.STATUS, 'ONLINE', ' Y', ' N')             tbsStat,
nvl(a.BYTES /1024/1024, 0)                          tbsMsize,
nvl(w.BYTES /1024/1024, 0)                       tbsMsizeMax,
nvl(a.BYTES - nvl(f.BYTES, 0), 0)/1024/1024         usedSize,
nvl((a.BYTES - NVL(f.BYTES, 0)) / a.BYTES * 100, 0) usedPct,
case
when nvl(w.BYTES /1024/1024, 0) != 0 then nvl((a.BYTES - NVL(f.BYTES, 0)) / nvl(w.BYTES, 0) * 100, 0) 
else nvl((a.BYTES - NVL(f.BYTES, 0)) / a.BYTES * 100, 0)
end usedTotPct
from DBA_TABLESPACES d,
(select TABLESPACE_NAME, sum(BYTES) bytes
from DBA_DATA_FILES
group by TABLESPACE_NAME) a,
(select TABLESPACE_NAME, sum(BYTES) bytes
from DBA_FREE_SPACE
group by TABLESPACE_NAME) f,
(select TABLESPACE_NAME, sum(case when (AUTOEXTENSIBLE='YES') then MAXBYTES else BYTES end) bytes
from DBA_DATA_FILES group by TABLESPACE_NAME) w 
where d.TABLESPACE_NAME = a.TABLESPACE_NAME(+)
and d.TABLESPACE_NAME = f.TABLESPACE_NAME(+)
and d.TABLESPACE_NAME = w.TABLESPACE_NAME(+)
and not (d.EXTENT_MANAGEMENT = 'LOCAL' and d.CONTENTS = 'TEMPORARY')
union all
select
--decode( greatest(90,nvl(t.BYTES/a.BYTES*100,0)), 90, 'TAILLE_TBS_OK', 'WARNING_TBS_FULL' ) STATUT,
case
when d.TABLESPACE_NAME like 'SYS%'                            then '   NO_CHECK'
when 
case
when nvl(w.BYTES /1024/1024, 0) != 0 then nvl(t.BYTES / nvl(w.BYTES, 0) * 100, 0)
else nvl(t.BYTES / a.BYTES * 100, 0)
end > 95
then '!! ALERT_TBS   > 95%'
when
case
when nvl(w.BYTES /1024/1024, 0) != 0 then nvl(t.BYTES / nvl(w.BYTES, 0) * 100, 0)
else nvl(t.BYTES / a.BYTES * 100, 0)
end > 85
then '!  WARNING_TBS > 85%'
else '   TBS_SIZE_OK'
end STATUT,
d.TABLESPACE_NAME                                   tbsName,
decode (d.CONTENTS, 'PERMANENT', 'P',
'TEMPORARY', 'T',
'UNDO', 'U')               prmType,
decode (d.EXTENT_MANAGEMENT, 'LOCAL', 'L', 'D')     extMan,
decode (d.STATUS, 'ONLINE', ' Y', ' N')             tbsStat,
nvl(a.BYTES / 1024 / 1024, 0)                       tbsMsize,
nvl(w.BYTES /1024/1024, 0)                       tbsMsizeMax,
nvl(t.BYTES, 0)/1024/1024                           usedSize,
nvl(t.BYTES / a.BYTES * 100, 0)                     usedPct,
case
when nvl(w.BYTES /1024/1024, 0) != 0 then nvl(t.BYTES / nvl(w.BYTES, 0) * 100, 0)
else nvl(t.BYTES / a.BYTES * 100, 0)
end usedTotPct
from DBA_TABLESPACES d,
(select TABLESPACE_NAME, sum(BYTES) bytes
from DBA_TEMP_FILES
group by TABLESPACE_NAME) a,
(select TABLESPACE_NAME, sum(BYTES_CACHED) bytes
from V$TEMP_EXTENT_POOL
group by TABLESPACE_NAME) t,
(select TABLESPACE_NAME, sum(case when (AUTOEXTENSIBLE='YES') then MAXBYTES else BYTES end) bytes
from DBA_DATA_FILES group by TABLESPACE_NAME) w 
where d.TABLESPACE_NAME = a.TABLESPACE_NAME(+)
and d.TABLESPACE_NAME = t.TABLESPACE_NAME(+)
and d.TABLESPACE_NAME = w.TABLESPACE_NAME(+)
and d.EXTENT_MANAGEMENT = 'LOCAL'
and d.CONTENTS = 'TEMPORARY'
order by 1 desc, 2 asc;
Ouais, je sais, cette requête est mortelle ;-)

Oracle et les paramètres cachés...

Oracle est un petit cachottier qui comporte des paramètres "cachés". Il s'agit de tous les paramètres dont le nom commence par _.
En théorie, on ne doit setter ces paramètres que sur demande du support, mais j'ai découvert que parfois une simple note metalink suggère de setter l'un ou l'autre de ces paramètres...
Or, je n'aime pas setter un paramètre si je ne sais pas quelle valeur il a auparavant.

Consulter les paramètres cachés

J'ai trouvé une petite requête sur internet que j'ai améliorée à ma sauce pour donner ceci :
set lines 200
set pages 100
col NAME format a20
col TYPE format 9999
col VALUE format a20
col ISDEFAULT format a20
SELECT ksppinm AS NAME,ksppity AS TYPE,ksppstvl AS VALUE,ksppstdf AS ISDEFAULT
FROM x$ksppi x INNER JOIN x$ksppcv y ON (x.indx = y.indx)
Finalement, le TYPE n'est pas une info très intéressante, ou alors il faudrait refaire une jointure avec la table des types pour savoir ce qu'elle vaut vraiment.
Il n'y a plus qu'à rajouter votre clause 'WHERE ksppinm LIKE '''.
Les noms de paramètres sont stockées en minuscules.

Setter un paramètre caché

Comme pour un paramètre normal, il faut utiliser l'ordre ALTER SYSTEM SET... Il suffit de penser à mettre le nom du paramètre caché entre double quotes.

Faire redémarrer l'observer au reboot de la machine

Ben oui, c'est bête. Vous avez installé votre Data Guard, votre observer est démarré, tout baigne! Mais la machine reboot et l'observer ne se lance pas automatiquement! Ben oui, vous avez oublié ce point de détail.

Scripter

On commence par faire un script pour démarrer l'observer. (Appelons-le start_observer.sh, au hasard.)
export PATH="$PATH:oracle_home/bin";
export ORACLE_HOME="oracle_home";

dgmgrl -logfile logfile  << eof
connect /@alias
stop observer;
start observer;
eof
Tant qu'on y est, on va aussi scripter l'arrêt :
export PATH="$PATH:oracle_home/bin";
export ORACLE_HOME="oracle_home";

dgmgrl -logfile logfile  << eof
connect /@alias
stop observer;
eof
On remarquera l'utilisation intelligente des wallets ;-).

init.d

On va ensuite dans le magnifique répertoire init.d et on y met notre superbe script (dont on est très fier(e)), son petit nom est oraobserver chez moi) :
#!/bin/bash
### BEGIN INIT INFO
# Provides: oraobserver
# Required-Start:
# Required-Stop:
# Default-Start:  3 4 5
# Default-Stop: 0 1 6
# Short-Description: Lance l'observer Oracle
# Description: Lance l'observer de la config Data Guard
### END INIT INFO
#
# source function library
. /etc/rc.d/init.d/functions


RETVAL=0


start() {
echo -n $"Starting Oracle Observer : \n"
nohup /home/oracle/start_obs.sh &
}


stop() {
echo -n $"Stopping Oracle Observer: \n"
/home/oracle/stop_obs.sh
RETVAL=$?
}


restart() {
stop
start
}


case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 1
esac


exit $RETVAL
On n'oublie pas de rendre le fichier exécutable.

Test

On peut tester le script en faisant :
/etc/init.d/oraobserver stop
/etc/init.d/oraobserver start
/etc/init.d/oraobserver restart

chkconfig

Ensuite, il suffit de dire à chkconfig d'ajouter ce service au démarrage :
chkconfig --add oraobserver

On peut ensuite vérifier les niveaux de démarrage en faisant :

chkconfig --list

Jouer avec les Wallet

Oracle permet de stocker les mots de passe dans des "wallets" sécurisés ce qui permet par la suit de se connecter sans saisir ni login ni mot de passe.

Création du Wallet

On commence par créer le wallet et son mot de passe. (Il faut que le mot de passe satisfasse les conditions de sécurité Oracle)
mkstore -wrl "dir" -create
dir est le répertoire où vous allez stocker tous les fichiers du Wallet

Ajout d'un alias

On ajout un alias au Wallet ainsi :
mkstore -wrl dir -createCredential ALIAS user psswd
Au prompt, il faut donner le mot de passe du wallet.

Il faut ensuite rajouter une entrée dans le tnsnames qui comporte l'alias donné au Wallet.

Liste des alias

Pour lister les alias déjà connus :
mkstore -wrl dir -listCredential
Au prompt, il faut donner le mot de passe du wallet.

Supprimer un alias

C'est tout simplement :
mkstore -wrl dir -deleteCredential ALIAS
Au prompt, il faut donner le mot de passe du wallet.

Créer un user en lecture seule sur un autre schéma

C'est une demande fréquente : les développeurs souhaitent avoir un user en lecture seule sur les objets d'un autre schéma...

Le rôle

Encore et toujours, on commence par créer le rôle correspondant à ces droits :
CREATE ROLE role_name NOT IDENTIFIED;
GRANT CONNECT TO role_name;
GRANT CREATE SESSION TO role_name;
GRANT RESOURCE TO role_name;
GRANT CREATE SYNONYM TO role_name;
GRANT UNLIMITED TABLESPACE TO role_name;
Il faut ensuite récupérer les GRANT SELECT à effectuer, c'est possible grâce à ces requêtes :
SELECT 'GRANT SELECT ON schema.'||TABLE_NAME||' TO role_name;' FROM dba_tables WHERE owner='owner';
SELECT 'GRANT SELECT ON schema.'||VIEW_NAME||'TO role_name;' FROM dba_views WHERE owner='owner';

Le user

On crée alors le user :
CREATE USER user_name IDENTIFIED BY pswd DEFAULT TABLESPACE tbs_name;Et on le grant :
GRANT role TO user;
GRANT UNLIMITED TABLESPACE TO user;

Synonymes

la création de synonymes n'est pas obligatoire mais facilite la vie du user... Autant le faire ! Il faut exécuter le résultat de ces deux requêtes :
SELECT 'CREATE SYNONYM ' ||TABLE_NAME|| ' FOR schema.' ||TABLE_NAME||';' FROM ALL_TABLES WHERE OWNER='owner';
SELECT 'CREATE SYNONYM ' ||VIEW_NAME|| ' FOR schema.' ||VIEW_NAME||';' FROM ALL_VIEWS WHERE OWNER='owner';
Et voilà!

Gestion des users "standards" sous Oracle

Tu es content, tu as fini d'installer et de configurer ta plateforme Oracle... Et là, on te demande un user standard avec tous les droits sur son schéma.
Comme d'habitude tu ne fais que de la migration, tu ne sais pas trop comment faire...
Respire, ça va bien se passer!

Les rôles, pensez-y!

Les rôles ont été créés par Oracle pour simplifier l'attribution des mêmes droits à plusieurs utilisateurs. Et même si aujourd'hui on ne te demande qu'un seul utilisateur, tu auras l'air malin si dans 2 ans on t'en demande un autre avec les mêmes droits!

Voilà les droits à "granter" pour obtenir un rôle permettant un accès total à tous les objets de son propre schéma :
CREATE ROLE role_name NOT IDENTIFIED;
GRANT CONNECT TO role_name;
GRANT CREATE SESSION TO role_name;
GRANT RESOURCE TO role_name;
GRANT CREATE CLUSTER TO role_name;
GRANT CREATE INDEXTYPE TO role_name;
GRANT CREATE OPERATOR TO role_name;
GRANT CREATE PROCEDURE TO role_name;
GRANT CREATE SEQUENCE TO role_name;
GRANT CREATE TABLE TO role_name;
GRANT CREATE TRIGGER TO role_name;
GRANT CREATE TYPE TO role_name;
GRANT UNLIMITED TABLESPACE TO role_name;

Et les tablespaces ???

Ne pas oublier de créer des tablespaces pour mettre les données du user. Cette étape est facultative, mais recommandée.
CREATE TABLESPACE tbs__dbo_indx01 DATAFILE '+_INDX' SIZE your_size AUTOEXTEND ON NEXT your_next_extend_size MAXSIZE your maxsize_or_unlimited;

CREATE TABLESPACE your_tbs_name DATAFILE 'your_datafile_path' SIZE your_datafile_size AUTOEXTEND ON  NEXT your_next_extend_size MAXSIZE your_max_size_or_unlimited;

Enfin, le user

Il n'y a plus qu'à créer le user
CREATE USER user_name IDENTIFIED BY your_psswd DEFAULT TABLESPACE your_tbs_name ;
Et lui donner les droits :
GRANT your_role TO user;
GRANT UNLIMITED TABLESPACE TO user;

Archive logs

Ce qu'on appelle couramment des archive logs en Oracle sont en fait des archives des redo logs. C'est l'équivalent des WAL de postgreSQL.

Purge

Bien sûr ces archive logs s'amoncellent; Le mieux est de purger les fichiers obsolètes grâce à la commande suivante (sous Rman).
DELETE OBSOLETE;
(Hé oui, des fois Oracle, c'est simple).
Le mieux est de rajouter cette commande après chaque sauvegarde.
Si Oracle était totalement planté suite à full, il y a de grandes chances qu'rman refuse de se connecter à votre base. Vous pouvez alors supprimer les fichiers les plus anciens à la main (sous ASM ou sur filesystem) et ensuite n'oubliez surtout pas d'exécuter cette commande :
CROSSCHECK ARCHIVELOG ALL
(Sous ASM, les archive logs sont "cachés" sous des répertoires ayant pour format yyyy_mm_dd).
Si vous ne trouvez pas d'archive logs obsolètes et que vous avez vraiment besoin de place, vous pouvez aussi tenter la commande suivante (en vous assurant que le chef de projet est conscient des risques) :
DELETE ARCHIVELOG ALL COMPLETED BEFORE 'your_date';

Politique de retention

Comment fait Rman pour savoir si un fichier est obsolète ? C'est là qu'il faut aller voir du côté de la politique de rétention. Vous pouvez soit définir un nombre de fichiers à conserver, soit définir une fenêtre de temps à conserver.
On peut voir sa politique de rétention en faisant
SHOW RETENTION POLICY;
Par défaut, Rman considère qu'un seul backup de chaque fichier est nécessaire.
Voilà comment lui dire de conserver n version(s) de chaque fichier :
CONFIGURE RETENTION POLICY TO REDUNDANCY n;
Voilà pour donner une durée de rétention de n jours :
CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF n DAYS;

Politique de suppression

Ah bah oui, pourquoi il ferait pas tout tout seul notre Rman adoré?
Par défaut la politique de suppression n'est pas settée. Vous pouvez décider par exemple de supprimer tous les archives logs d'une instance maître s'ils ont été appliqués sur la standby:
CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY;

Vérifier qu'une standby applique bien les logs (Oracle)

J'ai testé ces vérifications sur une standby physique, je ne suis pas sûre que cela fonctionne sur une standby logique.

Numéro de séquence

On commence par vérifier le numéro de séquence en cours avec cette requête sur le maître et l'esclave :
ARCHIVE LOG LIST
Il va répondre quelque chose de ce genre :
Database log mode              Archive Mode
Automatic archival             Enabled
Archive destination            USE_DB_RECOVERY_FILE_DEST
Oldest online log sequence     2794
Next log sequence to archive   2796
Current log sequence           2796
On peut ensuite demander au maître de changer de numéro de séquence :
ALTER SYSTEM SWITCH LOGFILE;
Et revérifier les numéros de séquence!

Mrp0

Le process qui applique les redos sur la standby s'appelle mrp0. Si vous requêtez la vue v$managed_standby, vous pourrez voir dans quel état il est :
SELECT process, client_process, sequence#, status FROM V$managed_standby WHERE process='MRP0';
S'il vous répond "no rows selected", il faut le démarrer avec un ordre du genre :
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT;
S'il vous répond "Applying log", tout va bien.

Recovery mode

Vous pouvez vérifier le "recovery mode" ainsi :
SELECT recovery_mode FROM v$archive_dest_status WHERE dest_id=2 ;

Quelques statistiques

Voici deux requêtes vous permettant de récupérer des stats parfois utiles :
SELECT * FROM v$dataguard_stats;
SELECT start_time, item, units, sofar FROM V$recovery_progress ORDER BY 1, 2;

Restaure d'une sauvegarde full avec Rman

Ça peut sembler le B.A.-BA mais c'est pas si simple...

Prérequis

Vous aurez besoin du DBID de votre base avant de pouvoir faire la sauvegarde... Vous pouvez le récupérer grâce à cette simple requête SQL (possible dès l'état MOUNT):
SELECT dbid FROM v$database;
Et si la base ne peut pas être montée (pb de controlfiles par exemple) ?
J'espère que vous avez gardé les logs de votre sauvegarde full... C'est ce qui devrait vous sauver!
connected to target database: YYY (DBID=xxx)

La procédure

Toutes les actions suivantes peuvent être effectuées sous rman. il n'est pas nécessaire de se connecter à sqlplus pour effectuer les startup ou open de la base de données. On suppose que la base de données est arrêtée et droppée.
  1. Settage du dbid
  2. SET dbid=xxx;
  3. démarrage en NOMOUNT de la base de données
  4. STARTUP NOMOUNT;
  5. Restaure des controlfiles
  6. RUN 
    {
    ALLOCATE CHANNEL t1 TYPE='your_type';
    RESTORE CONTROLFILE FROM TAG 'your_tag';
    RELEASE CHANNEL t1;  
    }
  7. Passage de la base en mode MOUNT
  8. ALTER DATABASE MOUNT;
  9. Restore de la base
  10. RUN 
    { 
    ALLOCATE CHANNEL t1 TYPE='your_type';  
    RESTORE DATABASE FROM TAG 'your_tag'; 
    RELEASE CHANNEL t1;  
    }
  11. Ouverture de la base
  12. ALTER DATABASE OPEN RESETLOGS;
Dans mon exemple, la sauvegarde full a a été faite à froid avec un tag.

Expdp (Export avec datapump)

Expdp est le pendant de pg_dump pour Oracle. L'outil d'import et l'outil d'export sont regroupés pour Oracle sous le joli nom de datapump.

Pré-requis

Pour utiliser datapump, vous devez avoir un directory Oracle. Par défaut, le directory DATA_PUMP_DIR est utilisé, mais seuls les users administrateurs ont accès à ce répertoire.

export full

Avec l'option FULL=y, expdp exporte la totalité du dump de la base source. Il faut avoir les droits EXP_FULL_DATABASE pour pouvoir effectuer cette commande.
expdp system/password DIRECTORY=expdp_dir DUMPFILE=export_name FULL=y LOGFILE=log_name

Import de schéma(s)

Vous pouvez décider de n'exporter qu'un ou quelques schéma(s) d'une base. pour ce faire, il faut préciser le(s)quel(s) dans le paramètre SCHEMA. Vous pouvez spécifier plusieurs schémas en les séparant par des virgules.
expdp system/password DIRECTORY=expdp_dir DUMPFILE=export_name SCHEMAS=schema_name LOGFILE=log_name

Export de table(s)

Comme vous l'aurez deviné, vous devrez utiliser le paramètre TABLES. Vous pouvez spécifier plusieurs tables en les séparant par des virgules
expdp system/password DIRECTORY=expdp_dir DUMPFILE=export_name TABLES=table_name LOGFILE=log_name

Autres options utiles

L'option CONTENT={ALL | DATA_ONLY | METADATA_ONLY} permet de sélectionner un import des données seulement.

Pour aller plus loin

La documentation complète est disponible ici :
10gr2:
http://download.oracle.com/docs/cd/B19306_01/server.102/b14215/dp_export.htm#i1006293
11gr2:
http://download.oracle.com/docs/cd/E11882_01/server.112/e16536/dp_export.htm#i1006293

PostGIS

PostGIS est le cartouche de données géographiques de postgreSQL. C'est le pendant d'Oracle spatial (que je connais nettement moins).

Création d'une base de données géographiques

Pour créer une base de données géographique, c'est relativement simple : Il suffit de créer une base de données normale et d'y jouer les scripts lwpostgis.sql et spatial_ref_sys.sql (généralement dans le répertoire share d'installation de votre version de postgreSQL).

Ces scripts créent les fonctions, objets, procédures et tables nécessaires à la manipulation de données géographiques.

Créer une table comportant des données géographiques

C'est tout simple : on commence par créer une table normale (CREATE TABLE...) et on ajoute ensuite la (ou les) colonne(s) géographique(s) à la main grâce à :

AddGeometryColumn(
  schema_name,
  table_name,
  column_name,
  srid,
  type,
  dimension
)

Spatial_ref_sys et geometry_columns

PostGIS utilise deux tables pour fonctionner : spatial_ref_sys et geometry_columns.
La table spatial_ref_sys comporte les colonnes suivantes :
DEMO_GEO=# \d spatial_ref_sys
         Table "public.spatial_ref_sys"
  Column   |          Type           | Modifiers
-----------+-------------------------+-----------
 srid      | integer                 | not null
 auth_name | character varying(256)  |
 auth_srid | integer                 |
 srtext    | character varying(2048) |
 proj4text | character varying(2048) |
Indexes:
    "spatial_ref_sys_pkey" PRIMARY KEY, btree (srid)

Cette table comporte les données nécessaires à l'utilisation de plus de 3000 systèmes de projections différents. A priori, vous n'aurez pas besoin de mettre les mains dedans. Elle est générée et peuplée par le script spatial_ref_sys.sql.

La table geometry_columns va beaucoup plus nous intéresser lorsqu'il s'agira de jouer au rebouteux pour données géographiques malades...
Voici la description de cette table :
DEMO_GEO=# \d geometry_columns
Table "public.geometry_columns"
     Column       |          Type          | Modifiers
-------------------+------------------------+-----------
f_table_catalog   | character varying(256) | not null
f_table_schema    | character varying(256) | not null
f_table_name      | character varying(256) | not null
f_geometry_column | character varying(256) | not null
coord_dimension   | integer                | not null
srid              | integer                | not null
type              | character varying(30)  | not null
Indexes:
"geometry_columns_pk" PRIMARY KEY, btree (f_table_catalog, f_table_schema, f_table_name, f_geometry_column)

Cette table comporte toutes les données concernant les colonnes de types géométriques qui se trouvent dans votre base. Exportez seulement vos schémas ou tables comportant vos données géographiques en omettant les données de cette table et vous aurez de jolies surprises lors de votre import dans une autre BDD.

Voici la description des colonnes de cette table :
  • f_catalog, f_schema, f_table et f_geometry_column permettent d'identifier avec précision la table (f_catalog est toujours vide)
  • coord_dimension permet de savoir s'il s'agit de données en 2D, 3D ou 4D
  • srid est le srid du système de projection utilisé (cf spatial_ref_sys)
  • type permet de savoir le type de données géométriques stockées (point, lignes, polygones...)

Ajouter manuellement des données dans geometry_columns

Vous avez fait des exports/imports de données dans une nouvelle base mais il vous manque les données de geometry_columns. Malheureusement votre sauvegarde est corrompue et vous ne savez pas comment récupérer les données de geometry_columns. (Ne riez pas, ça arrive!)

Pour chaque table avec des données géographiques, vous trouverez facilement les données à mettre dans f_schema, f_table et f_geometry_column.

Pour ce qui est des données à mettre dans coord_dimension et type, il va falloir être plus sioux.
Votre alliée se nomme pg_constraint. pg_constraint est une table du catalogue qui regroupe toutes les données liées aux contraintes.

DEMO_GEO=# \d pg_constraint
    Table "pg_catalog.pg_constraint"
    Column     |    Type    | Modifiers
---------------+------------+-----------
 conname       | name       | not null
 connamespace  | oid        | not null
 contype       | "char"     | not null
 condeferrable | boolean    | not null
 condeferred   | boolean    | not null
 conrelid      | oid        | not null
 contypid      | oid        | not null
 confrelid     | oid        | not null
 confupdtype   | "char"     | not null
 confdeltype   | "char"     | not null
 confmatchtype | "char"     | not null
 conkey        | smallint[] |
 confkey       | smallint[] |
 conpfeqop     | oid[]      |
 conppeqop     | oid[]      |
 conffeqop     | oid[]      |
 conbin        | text       |
 consrc        | text       |
Indexes:
    "pg_constraint_oid_index" UNIQUE, btree (oid)
    "pg_constraint_conname_nsp_index" btree (conname, connamespace)
    "pg_constraint_conrelid_index" btree (conrelid)
    "pg_constraint_contypid_index" btree (contypid)

Pour chaque colonne de type géographique, vous aurez 3 contraintes, une pour le srid, une pour la dimension et une pour le type de géométrie.
Il ne vous reste donc plus qu'à lancer la requête magique suivante :
SELECT c.consrc
FROM pg_constraint c
  INNER JOIN pg_class r ON c.conrelid = r.oid
  INNER JOIN pg_attribute a ON a.attnum = ANY (c.conkey)
WHERE r.relname = 'table_avec_colonne_geo'
  AND a.attname = 'colonne_geo'
  AND c.conname LIKE 'enforce%'

Vous recevrez un résultat de ce type :
        consrc               |         conname
----------------------------------------------------------+
 (srid(the_geom) = 4326)     | enforce_srid_the_geom
 (ndims(the_geom) = 2)       | enforce_dims_the_geom
 ((geometrytype(the_geom) =  | enforce_geotype_the_geom
 'POLYGON'::text) OR         |
 (the_geom IS NULL))         | 
Vous trouvez donc le SRID (4326), la dimension (2) et le type (POLYGON).

Impdp (Import avec datapump)

Impdp est le pendant de pg_restore pour Oracle. L'outil d'import et l'outil d'export est regroupé pour Oracle sous le joli nom de datapump.

Pré-requis

Pour utiliser datapump, vous devez avoir un directory Oracle. Par défaut, le directory DATA_PUMP_DIR est utilisé, mais seuls les users administrateurs ont accès à ce répertoire.

Import full


Avec l'option FULL=y, impdp recharge la totalité du dump dans la base cible. Il faut avoir les droits IMP_FULL_DATABASE pour pouvoir effectuer cette commande.
impdp system/<password> DIRECTORY=expdp_dir DUMPFILE=<export_name> FULL=y LOGFILE=<log_name>

Import de schéma(s)


Vous pouvez décider de n'importer qu'un ou quelques schéma(s) d'une base. pour ce faire, il faut préciser le(s)quel(s) dans le paramètre SCHEMA. Le fichier dump utilisé peut être un dump full, un dump de schéma(s), un dump de table(s) ou un dump de tablespace(s). Vous pouvez spécifier plusieurs schémas en les séparant par des virgules.
impdp hr/hr DIRECTORY=expdp_dir DUMPFILE=<export_name> SCHEMAS=<schema_name> LOGFILE=<log_name>

Import de table(s)


Comme vous l'aurez deviné, vous devrez utiliser le paramètre TABLES. Vous pouvez spécifier plusieurs tables en les séparant par des virgules.
impdp system/<password> DIRECTORY=expdp_dir DUMPFILE=<export_name> TABLES=<table_name> LOGFILE=<log_name>

Autres options utiles

L'option CONTENT={ALL | DATA_ONLY | METADATA_ONLY} permet de sélectionner un import des données seulement.
L'option REMAP_SCHEMA=old_schema:new_schema permet de faire un mapping pour importer les données d'un schéma dans un autre.

Pour aller plus loin

La documentation complète est disponible ici :
10gr2: http://download.oracle.com/docs/cd/B19306_01/server.102/b14215/dp_import.htm#i1010670
11gr2: http://download.oracle.com/docs/cd/E11882_01/server.112/e16536/dp_import.htm#SUTIL903

Sqlplus

Sqlplus est un utilitaire en ligne de commandes qui permet d'exécuter des requêtes sur une base de données Oracle.
(On supposera que les fichiers de configuration, tnsnames.ora sur le client et sqlnet.ora et listener.ora sur le serveur sont correctement remplis.)

Connexion

Pour lancer sqlplus, il faut lui indiquer le login, le mot de passe et le nom de l'instance à laquelle vous voulez vous connecter (nom d'instance qui se trouve dans tnsnames.ora).
sqlplus login/password@instance
Pour un login avec le user sys, ne pas oublier de préciser " as sysdba" derrière le nom d'instance.
sqlplus sys/password@instance as sysdba
Attention, en Oracle 9 et inférieur, la chaîne de connexion est entre ".
sqlplus "login/password@instance"

Déconnexion

Pensez à vous déconnecter en quittant psql proprement. La simple méta-commande exit vous le permet.

Lancement d'un script

On peut exécuter un fichier sql avec sqlplus et récupérer la sortie dans un autre fichier :
sqlplus login/password@instance @fichier.sql 2>nomFichierTrace 1>&2

Requêtes SQL

Sous sqlplus, on peut lancer des requêtes SQL. Ces requêtes peuvent être écrites sur plusieurs lignes. Veillez cependant à terminer vos requêtes par un point virgule ( ; ) ou un antislash ( \ ).

Pour aller plus loin

La documentation complète de sqlplus est disponible ici :
10gr2 : http://download.oracle.com/docs/cd/B19306_01/server.102/b14357/toc.htm
11gr2 : http://download.oracle.com/docs/cd/E11882_01/server.112/e16604/toc.htm

Outil d'analyse des volumes pour postgreSQL

J'ai eu besoin d'un outil pour récupérer rapidement et facilement tous les volumes de données par table. j'ai donc bricolé un bout de perl qui a juste le mérite d'être efficace.

(Je laisse ensuite le soin à Excel et aux tableaux croisés dynamiques de me permettre de faire des rapport sur les volumes par schéma, ou par base.)
Voici donc cet outil perl :
#!/usr/bin/perl
use Getopt::Long;
use strict;
#-----------------------------------------------------------------------------

sub usage 
{
print <<EOUSAGE;
perl $0 --in <fichierIn>
--Help            : ce message
--in <fichier>    : Le chemin compet du fichier comportant les noms des bases à analyser

Voici la requête SQL permettant de générer le fichier d'entrée :
\\pset format unaligned
\\pset border 0
\\pset tuples_only
SELECT datname as Nom
FROM pg_database
WHERE datname <> 'postgres'
AND datname NOT LIKE 'template%'
ORDER BY pg_database_size(datname) DESC

EOUSAGE
exit;
}

#-----------------------------------------------------------------------------
#   Main
#-----------------------------------------------------------------------------
my $help=0;
my $in;

GetOptions ("in=s" => \$in,
            "Help" => \$help);
usage() if ($help);
usage() if (!defined($in));
usage() if ($in eq '');

open(lignes,"<$in");
my @ligne = <lignes>;
open(out,">analyse_bases.sql");

my $mabdd;

#Génération des ordres de formatage pour la sortie à l'exécution sur script SQL généré
print(out "\\pset format unaligned\n");
print(out "\\pset border 0\n");
print(out "\\pset tuples_only\n");
# On génère le fichier de sortie
print(out "\\! touch /pgsql/save/analyse_bases.txt\n");

use constant REQUETE_TAILLE => "SELECT table_catalog, table_schema, table_name, pg_relation_size(table_schema || '.' || table_name) As Taille_donnees, pg_total_relation_size(table_schema || '.' || table_name) As Taille_totale_octets, pg_size_pretty(pg_total_relation_size(table_schema || '.' || table_name)) As taille_totale FROM information_schema.tables ORDER BY Taille_totale_octets DESC;\n";

foreach $mabdd (@ligne)
{  
    chomp($mabdd);
    # Génération de l'ordre de connexion à la BDD
    print(out "\\c $mabdd\n");
    #On peut commencer à écrire dans le ficheir de sortie
    print(out "\\o | cat - >> /pgsql/save/analyse_bases.txt\n");
    #On lance la requeête
    print(out REQUETE_TAILLE);
    #On arrête d'écrire dans le fichier de sortie
    print(out "\\o\n");    
}
Pour l'usage, on commence par générer la liste des bases de l'instance et on la met dans un fichier texte. On appelle ensuite le script avec en argument ce fichier. Le script nous génère un sql qu'il faut ensuite jouer sur la base.
Il n'y a plus qu'à récupérer le fichier analyses.txt

Psql

psql est un utilitaire en ligne de commandes qui permet d'exécuter des requêtes sur une base de données postgreSQL.

Connexion

Pour lancer psql, il faut lui indiquer a minima l'IP sur laquelle se connecter, le port sur lequel se connecter et le user avec lequel se connecter.
psql -h <host> -p <port> -U <user>
Normalement, psql vous demandera à ce moment là le mot de passe du user.
Si votre user n’a pas le même nom qu’une base de données, il faudra préciser le nom de la base de données pour vous connecter.
psql -h <host> -p <port> -U <user> -d <base>
Attention, <host> est plus souvent l’adresse IP de service que le nom de la machine !

Déconnexion

Pensez à vous déconnecter en quittant psql proprement. La simple méta-commande \q vous le permet.

Lancement d'un script

On peut exécuter un fichier sql avec psql et récupérer les logs générés de cette manière :
psql -h <host> -p <port> -U <user> -d <base> -f <nomFichier> 2>nomFichierTrace 1>&2

Requêtes SQL

Sous psql, on peut lancer des requêtes SQL. Ces requêtes peuvent être écrites sur plusieurs lignes. Veillez cependant à terminer vos requêtes par un point virgule ( ; ). Voici le prompt SQL normal :
<nombase>=#
Voici le prompt SQL qui indique qu’il attend la suite de la requête :
<nombase>-#
A tout moment, vous pouvez utiliser la touche « tabulation » pour obtenir une complétion automatique des noms d’objets SQL.

Meta-commands

Psql permet aussi d’envoyer des « méta-commandes » qui peuvent se révéler très pratiques. Voici une liste des plus utilisées :
\i <fichier> permet d’exécuter un fichier de commandes sql
\c <base> permet de se connecter à une base de données
\l permet de lister toutes les bases de données d’une instance
\dt permet de lister toutes les tables d’une base de données
\? permet de lister les méta-commandes de psql
\o <fichier> permet de stocker dans un fichier les résultats d’une requête
\h <commande> permet d’avoir la syntaxe de la commande SQL passée en argument

Pour aller plus loin

La documentation complète de psql est disponible ici :
8.1.5 : http://docs.postgresqlfr.org/8.1/app-psql.html
8.3.3 : http://docs.postgresqlfr.org/8.3/app-psql.html

Liste et taille des bases, des schémas et des tables en postgreSQL

Lors de la vie d'un projet, il faut suivre un minimum l'évolution des volumétries des différentes bases, schémas et tables. (Notamment la préconisation postgreSQL est de ne pas avoir de table plus grande que la RAM).

Liste et taille des bases d'une intance

La fonction pg_database_size donne la taille d'une base de données en octet. la fonction pg_size_pretty transforme un nombre en octet en utilisant l'unité la plus appropriée pour une lecture humaine.
La table pg_database du catalogue postgreSQL vous donnera la liste des base d'une instance.
Voici donc la requête :
SELECT datname as Nom, 
pg_database_size(datname) as Taille_en_Octets, 
pg_size_pretty(pg_database_size(datname)) as Taille
FROM pg_database
ORDER BY Taille_en_Octets DESC

Liste et taille des tables d'une base de données

La fonction pg_relation_size donne la taille des données d'une table. La fonction pg_total_relation_size donne la taille totale d'une table, en incluant les index et les tables TOAST.
La vue du catalogue information_schema tables donne la liste des tables de la base de données à laquelle vous êtes connecté.

SELECT table_name, 
pg_relation_size(table_schema || '.' || table_name) As Taille_donnees,
pg_total_relation_size(table_schema || '.' || table_name) As Taille_totale
FROM information_schema.tables
ORDER BY Taille_totale DESC
Pour rajouter le nom de la base, il suffit de rajouter la colonne table_catalog.

Liste et taille des schémas d'une base de données

La fonction pg_relation_size donne la taille des données d'une table. La fonction pg_total_relation_size donne la taille totale d'une table, en incluant les index et les tables TOAST.
En sommant toutes ces tailles par schéma, on arrive aux tailles des schémas.
La vue du catalogue information_schema tables donne la liste des tables de la base de données à laquelle vous êtes connecté.

Voici donc la requête à jouer :
SELECT table_schema, 
SUM(pg_relation_size(table_schema || '.' || table_name)) As Taille_donnees,
SUM(pg_total_relation_size(table_schema || '.' || table_name)) As Taille_totale
FROM information_schema.tables
GROUP BY table_schema
ORDER BY Taille_totale DESC
Pour rajouter le nom de la base, il suffit de rajouter la colonne table_catalog.

Lors de ces additions, j'ai eu un souci avec le type de sortie de sum qui ne permettait pas d'appliquer le pg_size_pretty (ne s'applique qu'à un bigint), je propose donc faire une conversion explicite ainsi :
pg_size_pretty(SUM(pg_total_relation_size(table_schema || '.' || table_name))::bigint)

Pg_restore

Pg_restore est un utilitaire en ligne de commandes qui permet d’importer tout ou partie d’une base de données postgreSQL.

Simple décompression

Pg_restore permet de décompresser les exports effectués avec l’option –Fc.
pg_restore –Fc –f <logFile> dumpFile

Import

Pg_restore peut aussi se connecter à une instance et restaurer une base de données à partir du fichier d’export. Pour se faire, il faut connaître a minima l’IP de service, le port et le user à utiliser.
pg_restore –h <host> -p <port> -U <user> -d <base> fichierDump
Si l’export a été créé avec l’option de compression –Fc (comme je vous conseille de faire), il faut utiliser l’option –Fc également avec pg_restore.
pg_restore –h <host> -p <port> -U <user> -d <base> -Fc fichierDump
Attention, le user et la base de données doivent avoir été créés au préalable.

Options avancées

Comme son cousin pg_dump, pg_restore supporte de nombreuses options qui permettent d’affiner l’import. Voici les principales :
-a n’importe que les données, les tables doivent déjà exister
-c supprime les objets de la base de données avant de les recréer
-n <schema> permet d’importer uniquement les objets du schéma spécifié
-s permet de définir les objets sans importer les données
-t <table> seule la table spécifiée est importée

Pour aller plus loin

La documentation complète de pg_restore est disponible ici :
8.1.5 : http://docs.postgresqlfr.org/8.1/app-pgrestore.html
8.3.3 : http://docs.postgresqlfr.org/8.3/app-pgrestore.html

Pg_dump

Pg_dump est un utilitaire en ligne de commandes qui permet d’exporter tout ou partie d’une base de données postgreSQL.

Export

Pour exporter toute une base de données, il faut a minima l’IP de service et le port de l’instance qui comporte la base de données à exporter. Il est important d’avoir un user qui a a accès à tous les objets de ladite table.
pg_dump -h <host> -p <port> -U <user> -f <fichierDeSortie> <base>
Normalement, psql vous demandera à ce moment là le mot de passe du user.
Une base de données, ça peut être TRES gros, pour cette raison, je vous conseille très fortement d’utiliser la compression à la volée au format postgreSQL (option –Fc).
pg_dump -h <host> -p <port> -U <user> -f <fichierDeSortie> -Fc <base>
Attention, <host> est plus souvent l’adresse IP de service que le nom de la machine !

Options avancées

Pg_dump fournit une pléiade d’options dont voici les plus importantes
-a permet d’exporter uniquement les données
-Ft permet d’obtenir des fichiers SQL compressés en tar
-n <schema> permet d’exporter uniquement le schéma spécifié
-s seule la définition des objets est exporté
-t <table> seule la table spécifiée est exportée

Pour aller plus loin

La documentation complète de pg_dump est disponible ici : :
8.1.5 : http://docs.postgresqlfr.org/8.1/app-pgdump.html
8.3.3 : http://docs.postgresqlfr.org/8.3/app-pgdump.html

Mode Archivelog (Oracle 10g, 11g)

Tout d'abord il faut savoir qu'un redémarrage d'instance est nécessaire pour passer du mode noarchivelog au mode archivelog. Il faut donc planifier ça avec le client.
Vous pouvez voir dans quel mode vous êtes en faisant :
SELECT name, log_mode FROM v$database;

Pour passer d'un mode noarchivelog à un mode archivelog, vous devez :
  1. Vérifier les paramètres db_recovery_file_dest et log_archive_dest_1
  2. Arrêter la base
  3. La redémarrer en mode mount
  4. Modifier le paramétrage
  5. Ouvrir la base
Voilà les étapes :
SHOW PARAMETER db_recovery_file_dest
SHOW PARAMETER log_archive_dest_1
SHUTDOWN IMMEDIATE
STARTUP MOUNT
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;
Vous pouvez ensuite refaire la requête sur la vue v$database pour vous assurer que le changement a bien été pris en compte.
Pour passer en mode noarchivelog, on fait les étapes 2 à 5 :

SHUTDOWN IMMEDIATE
STARTUP MOUNT
ALTER DATABASE NOARCHIVELOG;
ALTER DATABASE OPEN;