Rechercher sur arkzoyd.com

17 avril 2008

Oracle 10.2 sur OpenVMS : Entre Alpha et Itanium...

J'ai séché pendant 2 jours sur un problème d'installation Oracle 10.2 sur OpenVMS Alpha, ouvert 3 SR et finalement résolu le dernier problème sans l'aide d'Oracle ; tous les voyants sont au vert et l'étape suivante, cette fois pour la production, se fera sur OpenVMS Itanium avec RAC.

J'aime bien OpenVMS et si vous cherchez des ressources Oracle 10g/OpenVMS, vous devez nous contacter. En fait, plus ça parait nécessiter une expérience d'Oracle hors du commun, plus il est probable que vous avez intérêt à venir nous en parler en commençant par nous envoyer un message (en français) ! Il faut dire que vous aurez alors la chance, comme moi, de travailler avec plusieurs personnes avec qui c'est toujours passionnant d'échanger et de construire des environnements de production autour d'Oracle 10g et 11g.

En bref et pour revenir au sujet de ce post, l'occasion n'est pas donnée assez souvent de mettre en œuvre Oracle10g RAC et OpenVMS alors, quand j'ai vu arriver le projet pour un de nos clients, j'ai tout fait pour que personne d'autre ne le prenne en charge. Tout ça remonte, pour moi, au temps où Daniel Clar, alors président de DECUS France, m'a donné mon premier et sans doute seul cours d'informatique que je n'ai jamais suivi à l'école. A l'époque, les labos avaient les 2 premiers Alpha en France mais je crois surtout que j'admirais le prof qui semblait toujours revenir du ski en plus d'être affable et plaisant (du moins, c'est le souvenir que j'en garde). Mais après tout, peut-être que ce bon karma pour OpenVMS est simplement dues les partie de TRON à 8 sur les stations VAX VMS ! C'est ringuard de dire ça en 2008 ?

Pourquoi ce post vous demandez-vous ?

La réponse tient dans cette petite phrase assassine en réponse à une demande de l'analyste qui traitait ma dernière SR et que je veux partager. Bien sur, si vous pouvez éviter de tomber dans la même situation lors de votre prochaine installation Oracle 10g sur OpenVMS, ce post ne sera pas inutile non plus.

Alors voila, quand vous êtes le sujet d'un problème, Oracle essaie de faire bénéficier de la solution à toutes les personnes qui accèdent à Metalink. C'est pour ça, en partie, que c'est si dur d'être le premier à tomber sur un problème et que Metalink est si riche. Et pourtant il semble bien que je soit le premier et seul à être tombé dans ce cas sur mon serveur du siècle dernier. Dans un soucis de faire partager mon expérience (et surtout la solution), l'analyste a demandé aux responsables du contenu d'Oracle sur OpenVMS à documenter ce cas; voilà la réponse :
Knowledge content not created because this issue is customer specific and would not benefit anyone else.
J'en conclus que les caractéristiques de mon installation d'OpenVMS est unique au monde, mais je vais quand même vous expliquer ce qui s'est passé...

Alors voila, après avoir trouver un moyen (sans SSH) d'ouvrir un display X sur le serveur et à travers le NAT, demandé les fichiers de réponse qui ne sont pas dans la distribution Alpha (Seulement Itanium) et appliqué le patch 5743818, j'ai installé le logiciel en quelques heures ; en fait, l'installeur à mis un peu plus de 3 heures pour pousser la distribution et linker Oracle. Et puis, les problèmes sérieux : impossible de lancer DBCA ! Et créer tout l'environnement de l'instance à la main, en plus d'être non supporté est à peu prêt impossible sans un exemple de script DCL généré par... DBCA !

DBCA.COM échoue avec le message Unrecognized option: -classpath [...]. Je passerai le détail de comment en arriver à la conclusion mais le fait est que Java SDK 1.5 était préalablement installé sur le serveur et que le logical JAVA$DISABLE_CMDFILE_WHITESPACE_PARSING doit être, j'imagine positionné par défaut avec l'environnement Java. DBCA, quant à lui, positionne l'environnement pour le JDK 1.4 qui vient avec la distribution mais ne "deassign" pas le logical. Pour résoudre ce problème, il suffit d'exécuter les commandes ci-dessous avec l'utilisateur Oracle :
show log JAVA$DISABLE_CMDFILE_WHITESPACE_PARSING

"JAVA$DISABLE_CMDFILE_WHITESPACE_PARSING" = "TRUE" (LNM$JOB_821F6040)

deassign/table=LNM$JOB_821F6040 JAVA$DISABLE_CMDFILE_WHITESPACE_PARSING

show log JAVA$DISABLE_CMDFILE_WHITESPACE_PARSING
Enfin, la première étape est finalement complétée. En plus au passage, plusieurs document intéressants sont apparus :
Bon, la pression et les attentes de chacun sont maintenant retombées. Je peux revenir sur du matériel tout dernier cri pour faire cette install RAC sur OpenVMS (Itanium cette fois); mais je ne m'attends pas à rencontrer trop de difficultés...

14 avril 2008

Une plugin MySQL pour Oracle Enterprise Manager !

Et gratuite en plus !

Kudos à Alex G. qui a fini par craquer sous la pression que nous lui avons tous mis pour sortir sa plugin MySQL pour Oracle Enterprise Manager Grid Control dès que possible. Avec des centaines de métriques, plusieurs rapports et même une gestion de configuration, si vous faites parti de ces DBA Oracle qui doivent faire du MySQL, vous trouverez sans doute un intérêt immédiat dans cette plugin qui vous permettra de tirer parti d'Oracle Grid Control avec vos bases MySQL.

Pour l'instant, c'est une Beta mais n'hésitez pas à partager vos réussites ou vos difficultés sur ce Post ou sur le Post de Pythian.

11 avril 2008

Vider la mémoire de Linux...

La "bonne" nouvelle à propos de Linux, c'est que le système Open Source utilise et anticipe l'utilisation de toutes les ressources auquel il a accès. C'est le cas en particulier de la mémoire ou de l'espace de swap. Bien sur, ça signifie aussi que, quand il s'agit de dire si l'utilisation de la mémoire est pertinente pour votre base Oracle, vous sachiez de quoi vous parlez. Prenons un exemple d'idées communément répandues :
Pour que l'utilisation de la mémoire soit optimale, votre OS ne doit pas utiliser de swap !
En fait, si vous voulez empêcher Linux d'utiliser l'espace de swap, au moins avec le kernel 2.6, vous ne trouverez sûrement pas d'autres méthodes que de ne pas activer cet espace (Si c'est une voie que vous voulez explorer, soyez attentif au fonctionnement de OOM Killer). Linux a plusieurs bonnes raisons d'utiliser l'espace de swap. L'une d'entre elle peut évidement être d'utiliser cet espace comme un espace temporaire pour défragmenter sa mémoire. En fait ce n'est pas la seule, pour des ressources qui doivent résider en mémoire mais qui ne sont pas utilisées, Linux est capable d'anticiper leur mise en swap, pour le cas où... Donc, même si votre mémoire n'est pas remplie à 100%, Linux utilisera la swap. La bonne nouvelle pour Oracle, c'est que ça lui permettra, si le besoin s'en fait sentir d'utiliser plus de mémoire le moment venu sans "swaper" puisque Linux aura anticipe cette opération.

Mais à force de digressions, nous n'arriverons pas à l'objet de ce post... Alors, si vous voulez comprendre plein de chose sur l'utilisation de la mémoire sur Linux, regardez la video de Christo sur le site de Pythian. En fait, ça fait toujours un peu suspect de faire de la pub pour le travail d'un collègue, mais en l'occurrence celui là est excellent. Vous y apprendrez, par exemple, si ce n'est pas déjà le cas, quel est l'intérêt des Huges Pages même sur un système 64 bits ou pourquoi le fait qu'ASM nécessite la mise en place d'une instance Oracle n'est pas un problème comparé à d'autres systèmes de fichiers.

Un autre "mythe", très répandu vient du fait que :
la plupart des systèmes de supervision vous alerte quand la mémoire approche les 100% d'utilisation !
Prenons un exemple concret. Voici un extrait de l'utilisation de la mémoire d'un système sur lequel Oracle est installé :
$ cat /proc/meminfo
MemTotal: 8111348 kB
MemFree: 319324 kB
Buffers: 82440 kB
Cached: 1782496 kB
SwapCached: 9416 kB
Active: 771644 kB
Inactive: 1633736 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 8111348 kB
LowFree: 319324 kB
SwapTotal: 8008360 kB
SwapFree: 7953384 kB
Dirty: 664 kB
Writeback: 0 kB
Mapped: 617392 kB
Slab: 86120 kB
CommitLimit: 9442592 kB
Committed_AS: 2604280 kB
PageTables: 27788 kB
VmallocTotal: 536870911 kB
VmallocUsed: 5388 kB
VmallocChunk: 536865067 kB
HugePages_Total: 2560
HugePages_Free: 1087
Hugepagesize: 2048 kB
La mémoire non utilisée est d'environ 300Mo comparé au 8 Go disponibles. Vous remarquez que la swap n'est pas vide et pourtant pratiquement la moitié de la mémoire est gâchée sur ce système. Pourquoi ?
  • D'abord les espaces "Cached" et "Buffers" sont des espaces qui servent de tampon mémoire pour les fichiers et les accès en mode caractère. Ces espaces peuvent être réclamés a 100% sans impacter Oracle (du moins, si vous ne bénéficiez pas de ces caches pour vos datafiles, i.e filesystemio_options est positionné à directio ou setall ou que vous utilisiez RAC !)
  • Ensuite les Huge Pages sont toujours allouées et portant ici environ 2 Go sont inutilisés (1087 pages de 2Mo chacune).
Bon mais tout ça vous le savez sans doute... alors qu'est-ce que vous pouvez peut-être apprendre de ce post ? Et bien :
  • http://linux-mm.org/LinuxMM est un wiki très intéressant qui pointent sur de nombreux aspects de la gestion de la mémoire de Linux
  • /proc/sys/vm/drop_caches permet de vider les caches memoire de Linux ; D'après la doc à partir d'un kernel 2.6.16 mais qui est présent sur mon RHEL4U6 (2.6.9-67.ELsmp). Bon, ca fonctionne sur Ubuntu Gibbon-Gutsy mais de la à jouer avec ça en prod !
  • /proc/slabinfo (man slabinfo) et slabtop (man slabtop) permettent de visualiser les informations sur l'utilisation de plusieurs structures par le noyau et notamment les inodes.
Alors disons que c'est un début. A vous de prendre le relais...

10 avril 2008

Database PITR et création/suppression de fichiers

Un scénario qui n'est pas spécialement bien décrit dans la documentation est celui ou vous faites un ADD ou un DROP d'un fichier de données, via une commande sur le tablespace et que, pour une obscure raison, vous décidez d'effectuer un point in time recovery (PITR) de votre base de données. Oui obscure au moins pour le DROP puisque, dans ce cas, effectuer l'opération pour un tablespace (TSPITR) est peut-être plus adapté.

Ajout d'un fichier...

Dans ce cas, à condition que les SCN des fichiers restaurés précédent la création du fichier et donc que l'ordre d'ajout du datafile soit dans les fichier de logs que vous allez appliquer, il n'y a rien à considérer. En effet, même si le fichier est référencé dans le fichier de contrôle courant, le SCN de création l'est aussi. Vous pouvez d'ailleurs visualiser cet information en interrogeant V$DATAFILE lorsque l'instance est montée (cf CREATION_CHANGE#).

La base Oracle pourra donc être ouverte en mode RESETLOGS même si le fichier n'est pas encore créé mais qu'il est référencé dans le fichier de contrôle courant. Voici un exemple ou vous faites un PITR avant la création du tablespace DEMO2 :
SQL> create restore point z;

SQL> create tablespace demo2
datafile size 20M;
Utilisez ensuite RMAN pour faire le recover de votre base de données avant la création du fichier :
RMAN> startup force mount;

Oracle instance started
database mounted

Total System Global Area 313860096 bytes

Fixed Size 1299624 bytes
Variable Size 268438360 bytes
Database Buffers 37748736 bytes
Redo Buffers 6373376 bytes

RMAN> restore database;

Starting restore at 10-APR-08
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=152 device type=DISK

creating datafile file number=6 name=+DGBLUJ/bluj/datafile/demo2.262.651679255
channel ORA_DISK_1: starting datafile backup set restore
channel ORA_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_DISK_1: restoring datafile 00001 to +DGBLUJ/bluj/datafile/system.266.651365655
channel ORA_DISK_1: restoring datafile 00002 to +DGBLUJ/bluj/datafile/sysaux.267.651365659
channel ORA_DISK_1: restoring datafile 00003 to +DGBLUJ/bluj/datafile/undotbs1.256.651365661
channel ORA_DISK_1: restoring datafile 00004 to +DGBLUJ/bluj/datafile/users.257.651365659
channel ORA_DISK_1: restoring datafile 00005 to +DGBLUJ/bluj/datafile/demo.263.651678757
channel ORA_DISK_1: reading from backup piece /u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_nnndf_TAG20080410T132249_3zwm7mln_.bkp
channel ORA_DISK_1: piece handle=/u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_nnndf_TAG20080410T132249_3zwm7mln_.bkp tag=TAG20080410T132249
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:01:35
Finished restore at 10-APR-08

RMAN> recover database until restore point z;

Starting recover at 10-APR-08
using channel ORA_DISK_1

starting media recovery

archived log for thread 1 with sequence 9 is already on disk as file /u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_9_3zwml9fz_.arc
archived log for thread 1 with sequence 10 is already on disk as file /u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_10_3zwmlj64_.arc
archived log for thread 1 with sequence 11 is already on disk as file /u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_11_3zwmmx5t_.arc
archived log for thread 1 with sequence 1 is already on disk as file /u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_1_3zwnwgng_.arc
channel ORA_DISK_1: starting archived log restore to default destination
channel ORA_DISK_1: restoring archived log
archived log thread=1 sequence=8
channel ORA_DISK_1: reading from backup piece /u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_annnn_TAG20080410T132456_3zwmcn8r_.bkp
channel ORA_DISK_1: piece handle=/u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_annnn_TAG20080410T132456_3zwmcn8r_.bkp tag=TAG20080410T132456
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_8_3zwoogbq_.arc thread=1 sequence=8
channel default: deleting archived log(s)
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_8_3zwoogbq_.arc RECID=25 STAMP=651679470
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_9_3zwml9fz_.arc thread=1 sequence=9
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_10_3zwmlj64_.arc thread=1 sequence=10
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_11_3zwmmx5t_.arc thread=1 sequence=11
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_1_3zwnwgng_.arc thread=1 sequence=1
media recovery complete, elapsed time: 00:00:02
Finished recover at 10-APR-08

RMAN> alter database open resetlogs;

database opened
Vous remarquez d'Oracle recrée le fichier associé au tablespace DEMO2 (Ligne en rouge), et qu'il n'y a ensuite aucun problème pour ouvrir la base de données. Un SELECT sur V$DATAFILE après l'ouverture de la base de données vous confirmera que le fichier a été supprimé.

Suppression d'un fichier...


Ce second cas est un peu plus compliqué, en effet, si le fichier est dans une sauvegarde et qu'il n'est plus dans le fichier de contrôle courant, Oracle ne peut pas le restaurer simplement. D'autre part, utiliser le fichier de backup du fichier de contrôle après la sauvegarde de la base de données a plusieurs inconvénients. Les backup des archivelogs en particulier peuvent ne être pas dans ce fichier et retrouver les morceaux peut devenir un vrai casse-tête. C'est surtout vrai avec des sauvegardes sur bandes. Bien sur, si vous avez un catalogue RMAN tous ça est sans objets... Enfin, le plus simple est sans doute de partir du "current" controlfile et de restaurer votre base de données jusqu'au moment désiré :
SQL> create restore point x;

Restore point created.

SQL> drop tablespace demo
including contents;

Tablespace dropped.

SQL> startup force mount;

ORACLE instance started.

Total System Global Area 313860096 bytes
Fixed Size 1299624 bytes
Variable Size 268438360 bytes
Database Buffers 37748736 bytes
Redo Buffers 6373376 bytes
Database mounted.
SQL> exit;

$ rman target /

Recovery Manager: Release 11.1.0.6.0 - Production on Thu Apr 10 21:35:00 2008

Copyright (c) 1982, 2007, Oracle. All rights reserved.

connected to target database: BLUJ (DBID=1975191514, not open)

RMAN> restore database;

Starting restore at 10-APR-08
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=150 device type=DISK

channel ORA_DISK_1: starting datafile backup set restore
channel ORA_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_DISK_1: restoring datafile 00001 to +DGBLUJ/bluj/datafile/system.266.651365655
channel ORA_DISK_1: restoring datafile 00002 to +DGBLUJ/bluj/datafile/sysaux.267.651365659
channel ORA_DISK_1: restoring datafile 00003 to +DGBLUJ/bluj/datafile/undotbs1.256.651365661
channel ORA_DISK_1: restoring datafile 00004 to +DGBLUJ/bluj/datafile/users.257.651365659
channel ORA_DISK_1: reading from backup piece /u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_nnndf_TAG20080410T212937_3zxhrc1p_.bkp

channel ORA_DISK_1: piece handle=/u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_nnndf_TAG20080410T212937_3zxhrc1p_.bkp tag=TAG20080410T212937
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:01:45
Finished restore at 10-APR-08

RMAN> recover database until restore point x;

Starting recover at 10-APR-08
using channel ORA_DISK_1

starting media recovery
media recovery complete, elapsed time: 00:00:01

Finished recover at 10-APR-08

RMAN> exit
Vous noterez que si vous interrogez V$DATAFILE avant l'open RESETLOGS le fichier manquant n'est pas référencé (Ce qui est logique puisqu'il a été supprime du current controlfile). En revanche, après l'open RESETLOGS, Oracle, sait qu'il y avait un fichier ; il l'a reconstruit grâce à son dictionnaire.
SQL> col name format a80
SQL> set lines 120
SQL> select file#, name
from v$datafile;

FILE# NAME
----- ------------------------------------------------
1 +DGBLUJ/bluj/datafile/system.266.651365655
2 +DGBLUJ/bluj/datafile/sysaux.267.651365659
3 +DGBLUJ/bluj/datafile/undotbs1.256.651365661
4 +DGBLUJ/bluj/datafile/users.257.651365659

SQL> alter database open resetlogs;

Database altered.

SQL> select file#, name
from v$datafile;
FILE# NAME
----- ------------------------------------------------------
1 +DGBLUJ/bluj/datafile/system.266.651365655
2 +DGBLUJ/bluj/datafile/sysaux.267.651365659
3 +DGBLUJ/bluj/datafile/undotbs1.256.651365661
4 +DGBLUJ/bluj/datafile/users.257.651365659
5 /u01/app/oracle/product/11.1.0/db_1/dbs/MISSING00005

SQL> exit
Maintenant que vous avez une référence au fichier et s'il est dans une sauvegarde, le restaurer et en faire le recover à partir de 10g devient un jeu d'enfant. Il faut simplement que vous décidiez du nom (ou de la destination en OMF) du fichier en positionnant le NEWNAME come ci-dessous :
RMAN> run
{
set newname for datafile 5 to '+dgbluj';
restore datafile 5;
switch datafile all;
}

executing command: SET NEWNAME
using target database control file instead of recovery catalog

Starting restore at 10-APR-08
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=150 device type=DISK

channel ORA_DISK_1: starting datafile backup set restore
channel ORA_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_DISK_1: restoring datafile 00005 to +DGBLUJ
channel ORA_DISK_1: reading from backup piece /u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_nnndf_TAG20080410T212937_3zxhrc1p_.bkp
channel ORA_DISK_1: piece handle=/u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_nnndf_TAG20080410T212937_3zxhrc1p_.bkp tag=TAG20080410T212937
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
Finished restore at 10-APR-08

datafile 5 switched to datafile copy
input datafile copy RECID=6 STAMP=651707407 file name=+DGBLUJ/bluj/datafile/demo.263.651707407
Et voila... reste à faire le recover, ce qui est autoatiquement fait jusqu'au SCN de l'open RESETLOGS puisque cette information relative à la dernière incarnation de votre base de données est dans le fichier de contrôle. Vous pourrez ensuite passer le fichier de données ONLINE :
RMAN> recover datafile 5;

Starting recover at 10-APR-08
using channel ORA_DISK_1

starting media recovery

archived log for thread 1 with sequence 4 is already on disk as file /u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_4_3zxjowz4_.arc
channel ORA_DISK_1: starting archived log restore to default destination
channel ORA_DISK_1: restoring archived log
archived log thread=1 sequence=3
channel ORA_DISK_1: reading from backup piece /u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_annnn_TAG20080410T213132_3zxhvx9b_.bkp
channel ORA_DISK_1: piece handle=/u01/app/oracle/flash_recovery_area/BLUJ/backupset/2008_04_10/o1_mf_annnn_TAG20080410T213132_3zxhvx9b_.bkp tag=TAG20080410T213132
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_3_3zxjzqof_.arc thread=1 sequence=3
channel default: deleting archived log(s)
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_3_3zxjzqof_.arc RECID=36 STAMP=651707447
archived log file name=/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_10/o1_mf_1_4_3zxjowz4_.arc thread=1 sequence=4
media recovery complete, elapsed time: 00:00:00
Finished recover at 10-APR-08

RMAN> sql 'alter database datafile 5 online';

sql statement: alter database datafile 5 online
L'inconvénient toutefois d'une telle approche est que si vous avez beaucoup d'archive logs à rejouer, il faut le faire 2 fois... Peut-être la solution après tout, c'est de faire attention à ce que vous supprimez ! Quoique, si vous lisez ce post, c'est peut-être à cause de quelqu'un d'autre.

09 avril 2008

2 mots à propos du recover PITR d'une base Oracle !

La documentation Oracle sans doute le meilleur des livres à propos de la base de données. En plus, il y a un nombre impressionnant de livres, plus de 100, ce qui est intarissable même pour les plus passionnés... Même si vous êtes assidu, le temps que vous les terminiez et la prochaine version n'est pas loin ;-).

Et pourtant, il y a quelques rares coquilles qui traînent. Ce post est à propos de l'une d'entre-elles lorsque vous effectuez un Database Point in Time Recovery (PITR). Suivez-le lien est vous noterez ce qui suit :
  • Cette section illustre un PITR de la base de données en utilisant le fichier de contrôle courant. Voici ce qui est écrit :
The control file is current. If you need to restore a backup control file, then see "Performing Recovery with a Backup Control File".
  • Oracle vous recommande (point 6) d'ouvrir la base de données en read-only pour valider qu'il n'y a pas de corruption logique :
6. Open the database read-only in SQL*Plus and perform queries as needed to ensure that the effects of the logical corruption have been reversed.
En fait, si vous êtes amené à effectuer un PITR de votre base de données, c'est peut-être justement parce que vos données ont été modifiées et dans ce cas l'ouvrir en lecture pourrait être une bonne idée, si seulement c'était possible :
Connected to:
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> alter database open read only;
alter database open read only
*
ERROR at line 1:
ORA-16005: database requires recovery
Et voila ! La documentation est fausse; vous ne pouvez pas ouvrir la base de données en read-only à partir du fichier de contrôle courant (cf la note #399276.1 pour plus de détails). Ou plutôt vous pouvez, si vous utilisez la commande FLASHBACK... ou si vous contournez le problème !

Alors, comment ouvrir la base de données en READ ONLY ?

Si vous mettez bout à bout le contenu de la note et ce que vous savez de la base de données, vous arriverez sûrement à la solution qui suit ou une solution approchante :

Etape 1 :
Créez un fichier "sauvegarde" du controlfile et montez la base de données sur ce fichier. On supposera que vous avez effectué votre recover de base de données à partir du current controlfile. Dans ce cas, votre instance est montée sur le fichier de contrôle :
  • Connectez-vous avec SQL*Plus,
  • Faites une copie du fichier de contrôle,
  • *Important* Notez la valeur courante du paramètre control_files
  • Changez le fichier spfile pour de manière à pointer sur la sauvegarde du fichier de contrôle
  • Arrêtez et redémarrez l'instance en mode MOUNT
$ sqlplus / as sysdba

SQL*Plus: Release 11.1.0.6.0 - Production on Wed Apr 9 21:01:10 2008

Copyright (c) 1982, 2007, Oracle. All rights reserved.

Connected to:
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> alter database
backup controlfile
to '+DGBLUJ/bluj/controlfile/backup.ctl';

Database altered.

SQL> show parameter control_files

NAME TYPE VALUE
------------- ------ -------------------------------------------------
control_files string '+DGBLUJ/bluj/controlfile/current.268.651365597',
'+DGBLUJ/bluj/controlfile/current.265.651365599'

SQL> alter system
set control_files='+DGBLUJ/bluj/controlfile/backup.ctl'
scope=spfile;

System altered.

SQL> startup force mount;
ORACLE instance started.

Total System Global Area 313860096 bytes
Fixed Size 1299624 bytes
Variable Size 268438360 bytes
Database Buffers 37748736 bytes
Redo Buffers 6373376 bytes
Database mounted.
Etape 2 :
Déterminez le premier SCN auquel vous pouvez faire votre recover pour positionnez le SCN du fichier de contrôle. Etant donné que vous avez fait déjà fait le recover des fichiers de données, cette information est dans les entêtes des fichiers de données. Vous pouvez déterminer cette première valeur avec la requête ci-dessous :
SQL> select CHECKPOINT_CHANGE# 
from v$datafile_header;

CHECKPOINT_CHANGE#
------------------
1308788
1308788
1308788
1308788
Toutes les lignes (1 par fichier de données) doivent avoir la même valeur : Celle du SCN que vous aller utiliser...

Etape 3 :
Effectuez le recover du fichier de contrôle ; vous n'avez besoin pour cela que du fichier d'archivelog qui contient le dit SCN. Pour cette opération, utilisez SQL*Plus comme dans l'exemple ci-dessous :
SQL> recover database            -
using backup controlfile -
until change 1308788

ORA-00279: change 1308788 generated at 04/09/2008 18:54:43 needed for thread 1
ORA-00289: suggestion :
/u01/app/oracle/flash_recovery_area/BLUJ/archivelog/2008_04_09/o1_mf_1_8_3ztl9sv
1_.arc
ORA-00280: change 1308788 for thread 1 is in sequence #8

Specify log: {=suggested | filename | AUTO | CANCEL}

Log applied.
Media recovery complete.
Et voila, vous pouvez ouvrir la base de données en lecture comme ci-dessous :
SQL> alter database open read only;

Database altered.
Etape 4 :
Avec le nouveau fichier de contrôle, vous devrez utilisez OPEN RESETLOGS pour ouvrir votre base de données et ceci, même si vous décidez de faire le recover de vos fichiers d'archive et de redo logs jusqu'au bout. Pour éviter ce problème, vous pouvez ré-utiliser le fichier de contrôle courant comme ci-dessous :
SQL> alter system 
set control_files='+DGBLUJ/bluj/controlfile/current.268.651365597',
'+DGBLUJ/bluj/controlfile/current.265.651365599'
scope=spfile;

System altered.

SQL> startup force mount;
Et vous voila revenu à votre point de départ ! On tourne en rond, quoique.

08 avril 2008

Pour prouver que ce n'est pas Oracle...

Il suffit de reproduire sans Oracle ! Et pour RAC, c'est la même chose... Alors voila si vous suspectez, un problème réseau sur l'interconnect, voici 2 programmes très utiles qui permettent d'échanger des données entre les serveurs du cluster via UDP.

Sur Linux, il suffit de les compiler chacun des fichiers comme dans l'exemple ci-dessous :
gcc server.c -o server
gcc client.c -o client
En 10 minutes, vous pourrez démontrer (ou pas !), qu'il y a un problème réseau sur l'interconnect. Evidemment, si ca marche juste un peu...

03 avril 2008

"cursor_sharing=similar" et "method_opt=>'FOR ALL COLUMNS SIZE AUTO'"

Faut-il collecter des histogrammes sur les colonnes quand vous utilisez cursor_sharing=SIMILAR ? Dans ce cas, que se passe-t-il si vous gardez la méthode de collecte par défaut des statistiques d'Oracle 11g ? Sur quoi s'appuie la méthode automatique de collecte des histogrammes sur les colonnes ? Voici quelques unes de ces questions qui sont abordées ou illustrées dans ce post... Quoique ce n'est que le sommet de l'iceberg !

Remarque :
Ces exemples ont été testés avec oracle 10.2.0.4 et 11.1.0.6 sur Linux x86 !

Partons d'un exemple simple...

Une table, deux colonnes, cursor_sharing=SIMILAR. Voila le test case :
create table demo(
col1 number,
col2 varchar2(1000)
);

begin
for i in 1..50000 loop
insert into demo(col1, col2)
values (i, rpad('A',999,'A'));
insert into demo(col1, col2)
values (1000, rpad('A',999,'A'));
end loop;
commit;
end;
/

create index demo_idx on demo (col1);

alter session set cursor_sharing=similar;
Examinons le cas #1, sans histogramme:

Avant d'exécuter les requêtes, nous allons collecter les statistiques, dans un premier cas en s'assurant qu'il n'y a pas d'histogramme (i.e. FOR ALL COLUMNS SIZE 1)
exec dbms_stats.gather_table_stats(user,                    -
'DEMO',method_opt=>'FOR ALL COLUMNS SIZE 1', -
cascade=> true)

col COLUMN_NAME format a6

select column_name, count(*)
from user_histograms
where table_name='DEMO'
group by column_name
order by column_name;


COLUMN COUNT(*)
------ ----------
COL1 2(*)
COL2 2

(*) Notez que s'il n'y a que 2 lignes pour une colonne dans ALL_HISTOGRAMS, cela signifie qu'il n'y a pas d'histogrammes (sauf s'il n'y a que 2 valeurs dans la colonne)

Une fois les statistiques collectées et vérifiées, vous pouvez lancer les requêtes suivantes dans SQL*Plus; n'utilisez pas un autre outils comme SQL*Developer parce que l'appel à dbms_xplan.display_cursor() référence l'ordre SQL précédent lancé dans la session et les outils "évolués" exécutent d'autres ordres SQL sans que vous ne soyez notifié(e). SQL*Plus, à moins que vous n'utilisiez des modes funky comme serveroutput on, n'a pas ce genre de comportement :
set pages 1000
set lines 180

select count(col2)
from demo
where col1=1001;

COUNT(COL2)
-----------
1

select * from table(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID d27tczp42j637, child number 0
-------------------------------------
select count(col2) from demo where col1=:"SYS_B_0"

Plan hash value: 1661014706

----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2 (100)|
| 1 | SORT AGGREGATE | | 1 | |
| 2 | TABLE ACCESS BY INDEX ROWID| DEMO | 2 | 2 (0)|
|* 3 | INDEX RANGE SCAN | DEMO_IDX | 2 | 1 (0)|
----------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("COL1"=:SYS_B_0)

select count(col2)
from demo
where col1=1000;

COUNT(COL2)
-----------
50001
select * from table(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID d27tczp42j637, child number 0
-------------------------------------
select count(col2) from demo where col1=:"SYS_B_0"

Plan hash value: 1661014706

----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2 (100)|
| 1 | SORT AGGREGATE | | 1 | |
| 2 | TABLE ACCESS BY INDEX ROWID| DEMO | 2 | 2 (0)|
|* 3 | INDEX RANGE SCAN | DEMO_IDX | 2 | 1 (0)|
----------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("COL1"=:SYS_B_0)
Vous remarquerez, non seulement que le SQL_ID sont les mêmes, puisque les valeurs littérales 1001 et 1000 sont substituées par une variable de type BIND générée par le système et qu'en outre (Et c'est le plus important pour cet exemple), le "CHILD CURSOR" est le même, i.e. 0. Vous pouvez valider ce dernier point en vous connectant DBA et en interrogeant la fixed view V$SQL pour le-dit SQL_ID comme ci-dessous :
select SQL_ID, CHILD_NUMBER
from v$sql
where sql_id='d27tczp42j637';

SQL_ID CHILD_NUMBER
------------- ------------
d27tczp42j637 0
Dans ce cas, les 2 ordres SQL sont considérés comme complètement identiques. Puisque les colonnes n'ont pas d'histogrammes, les plans pour les valeurs 1000 et 1001 seront forcément les mêmes. L'optimiseur en tient compte et utilise donc un seul curseur. C'est ce que la documentation décrit par cet section consacrée au cursor_sharing = SIMILAR, comme ci-dessous :
Causes statements that may differ in some literals, but are otherwise identical, to share a cursor, unless the literals affect either the meaning of the statement or the degree to which the plan is optimized.
Avant de passer au second exemple, videz le contenu de la shared pool :
alter system flush shared_pool;

select SQL_ID, CHILD_NUMBER
from v$sql
where sql_id='d27tczp42j637';

no rows selected
Examinons le cas #2, avec un histogramme:

Dans ce deuxième exemple et avant d'exécuter les requêtes, nous allons collecter les statistiques, en forçant la prise d'histogrammes sur <code>COL1 :
exec dbms_stats.gather_table_stats(user,                             -
'DEMO', method_opt=>'FOR COLUMNS COL1 SIZE SKEWONLY', -
cascade=> true)

col COLUMN_NAME format a6

select column_name, count(*)
from user_histograms
where table_name='DEMO'
group by column_name
order by column_name;

COLUMN COUNT(*)
------ ----------
COL1 131
COL2 2
Utilisez encore SQL*Plus et ré-exécutez les 2 ordres SQL précédents :
set pages 1000
set lines 180

select count(col2)
from demo
where col1=1001;

COUNT(COL2)
-----------
1

select * from table(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID d27tczp42j637, child number 0
-------------------------------------
select count(col2) from demo where col1=:"SYS_B_0"

Plan hash value: 1661014706

----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2 (100)|
| 1 | SORT AGGREGATE | | 1 | |
| 2 | TABLE ACCESS BY INDEX ROWID| DEMO | 2 | 2 (0)|
|* 3 | INDEX RANGE SCAN | DEMO_IDX | 2 | 1 (0)|
----------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("COL1"=:SYS_B_0)

select count(col2)
from demo
where col1=1000;

COUNT(COL2)
-----------
50001

select * from table(dbms_xplan.display_cursor());

-------------------------------------
SQL_ID d27tczp42j637, child number 1
-------------------------------------
select count(col2) from demo where col1=:"SYS_B_0"

Plan hash value: 2180342005

--------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
--------------------------------------------------------
| 0 | SELECT STATEMENT | | | 4127 (100)|
| 1 | SORT AGGREGATE | | 1 | |
|* 2 | TABLE ACCESS FULL| DEMO | 49213 | 4127 (1)|
--------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("COL1"=:SYS_B_0)
Dans ce cas, vous constaterez que le CHILD CURSOR diffère; la présence de l'histogramme force l'ordre SQL à un nouveau HARD PARSE. Cela semble évident puisque dans ce cas, le plan change aussi. Essayez avec d'autre valeurs et vous constaterez que le SQL donne lieu à un nouveau CHILD CURSOR pour chaque nouvelle valeur :
select count(col2)
from demo
where col1=1002;

COUNT(COL2)
-----------
1

select * from table(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID d27tczp42j637, child number 2
-------------------------------------
select count(col2) from demo where col1=:"SYS_B_0"

Plan hash value: 1661014706

----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2 (100)|
| 1 | SORT AGGREGATE | | 1 | |
| 2 | TABLE ACCESS BY INDEX ROWID| DEMO | 2 | 2 (0)|
|* 3 | INDEX RANGE SCAN | DEMO_IDX | 2 | 1 (0)|
----------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("COL1"=:SYS_B_0)
Dit autrement, la présence d'histogrammes empêche l'ordre SQL d'être ré-utilisé lorsque cursor_sharing=SIMILAR. Vous pouvez mettre en évidence cet utilisation de la SHARED POOL en interrogeant la fixed view V$SQL :
select SQL_ID, CHILD_NUMBER
from v$sql
where sql_id='d27tczp42j637';

SQL_ID CHILD_NUMBER
------------- ------------
d27tczp42j637 0
d27tczp42j637 1
d27tczp42j637 2
Autre remarque intéressante, si vous interrogez V$SQL_SHARED_CURSOR, vous ne saurez pas pourquoi les curseurs ne sont pas ré-utilisés ! (Exécutez la requête pour vous en persuader) :
select * 
from v$sql_shared_cursor
where sql_id='d27tczp42j637';
Un pas de plus: 2 mots sur "FOR ALL COLUMNS SIZE AUTO"

Ce post précédent illustre le fonctionnement de cette option de collecte des statistiques. Il faut simplement modérer mon enthousiasme de l'époque à la lumière de l'impact des histogrammes sur la réutilisation des curseurs avec cursor_sharing=SIMILAR. Vous pouvez également ajouter que la table SYS.COL_USAGE$ est en fait ce qui permet à DBMS_STATS de déclencher la prise d'histogrammes.

Conclusion

cursor_sharing=EXACT est la meilleure façon de voir le monde ! C'est d'autant plus vrai que beaucoup d'encre coule chaque jour au sujet des histogrammes ou du bind peeking dans la "blogosphère et sur les différents canaux de discussions Oracle". EXACT permet-il d'adresser tous les cas ? oui mais évidemment, cela suppose que les développeurs utilisent des BIND variables quand les curseurs doivent être réutilisés, qu'ils utilisent des littéraux quand il ne faut pas. Cela suppose que le monde soit parfait mais il ne l'est pas. C'est donc pour ça qu'il y a d'autres mode.

Peut-être que ce post vous aidera à appréhender un peu mieux l'impact de SIMILAR. Un rapide coup d'oeil à Metalink sur les bugs spécifiques à ce mode vous instruira également quant aux "autres risques" associés. Quant à savoir s'il faut ou non prendre des histogrammes...

Et si vous voulez encore creuser plus ?

D'abord, regardez ce post de Laurent Schneider pointant ASKTOM qui vous occupera sans doute un bon moment mais qui est passionnant et beaucoup plus creusé que ces quelques lignes !

Ensuite, enchaînez les tests sans et avec histogramme sans vider la shared pool, le résultat avec 10.2.0.4 est plus qu'intéressant (Je n'ai pas testé avec 11.1.0.6 mais je ne serais pas surpris s'il est identique). Je devine ce qu'il se passe mais je n'arrive pas à mettre en évidence les données associés. Enfin, examinez aussi la pertinence ou non de V$SQL_BIND_DATA .

Avant d'en finir...
drop table demo purge;