Blog

Blog
Affichage des articles dont le libellé est Disques. Afficher tous les articles
Affichage des articles dont le libellé est Disques. Afficher tous les articles

ORA-01654 sur SYSAUX

Je suis tombée sur une erreur ORA-01654 car le tablespace SYSAUX était plein. Je pense qu'il n'avait pas forcément été bien dimensionné au départ (il faut utiliser le script utlsyxsz pour estimer la taille nécessaire à SYSAUX).

Cependant, n'étant pas propriétaire de la base et n'ayant pas l'autorisation d'augmenter le-dit SYSAUX, j'ai du chercher à grappiller quelques Mo.

Utilisation de SYSAUX

Pour savoir ce qui utilise SYSAUX, il suffit de lancer cette requête :
SELECT  occupant_name "Coupable",
space_usage_kbytes/1048576 "Espace occupé (Go)",
schema_name "Schema"
FROM v$sysaux_occupants
ORDER BY 2 desc;
Dans mon cas, c'était AWR qui occupait plus de la moitié du SYSAUX.

AWR et droit d'utilisation

Pour ce client, AWR était d'autant plus inutile qu'il n'avait pas payé le tuning pack. Il n'avait donc pas le droit de générer et d'aller voir les rapports AWR. Il se trouve qu ele client ne savait même pas ce que c'était. j'ai donc eu l'autorisation de purger les snapshots.

Purge des snapshots

Pour purger les snapshots, il faut savoir quel est le plus ancien et quel est le plus récent. Cette requête donne ces infos.
SELECT snap_id, begin_interval_time, end_interval_time 
FROM SYS.WRM$_SNAPSHOT 
WHERE snap_id = ( SELECT MIN (snap_id) FROM SYS.WRM$_SNAPSHOT)
UNION 
SELECT snap_id, begin_interval_time, end_interval_time 
FROM SYS.WRM$_SNAPSHOT 
WHERE snap_id = ( SELECT MAX (snap_id) FROM SYS.WRM$_SNAPSHOT); 

Il faut ensuite réutiliser ces valeurs dans le bloc PL/SQL suivant :
BEGIN
  dbms_workload_repository.drop_snapshot_range(low_snap_id => 14241, high_snap_id=>14321);
END;

Récupérer la place libérée

Il faut ensuite shrinker la table pour pouvoir réellement récupérer la place (il faut être connecté en sys as sysdba).
alter table sys.WRH$_ACTIVE_SESSION_HISTORY shrink space cascade;

Et après ?

La procédure ci-dessus n'est qu'une procédure d'urgence. Il faudra prévoir rapidement d'envisager au moins une des actions suivantes :

  • Faire tourner le script utlsyxsz  pour évaluer le besoin réel d'espace de SYSAUX
  • Ajouter un datafile à SYSAUX (ou augmenter les datafiles existants)
  • Diminuer la fréquence et la rétention des snapshots d'AWR

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

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 ;-)

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;

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

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)