Canalblog
Editer l'article Suivre ce blog Administration + Créer mon blog
Publicité
Blog d'un DBA sur le SGBD Oracle et SQL
4 octobre 2019

Lancer une commande Linux avec un job Oracle - Launch a Linux command with an Oracle job



Introduction

Parfois il arrive que l'on ne trouve pas dans les milliers de vues Oracle l'information que l'on cherche! Soit elle n'existe pas (cas extrêmement rare) soit elle existe dans les vraies tables du dictionnaire de données comme TAB$, COL$ mais l'info n'est pas externalisée dans les vues comme DBA_TABLES, DBA_TAB_COLS (cas pour des infos vraiment très pointues) soit on n'a pas réussi à la localiser (cas le plus fréquent).

Il se peut aussi qu'un DBA préfère analyser ces informations venant d'un outil Linux qu'il connaît bien. Dans ce cas on est face à un autre problème, celui de l'historisation des données. Sauf erreur de ma part, il n'est par exemple pas possible de lancer la commande iostat dans le passé. Dans quel but? Par exemple pour comparer les vitesses d'écriture/lecture des disques durs entre il y a deux jours et aujourd'hui dans le cas où on constate un fort ralentissement de la base; après analyse le problème ne vient ni de la base ni du réseau; on s'oriente donc vers un problème de disque dur mais pour conclure il faut des données sur plusieurs jours et cela iostat ne le permet pas.

Oracle met à disposition des vues comme DBA_HIST_SEG_STAT, DBA_HIST_FILESTATXS etc etc mais parfois trop d'info tue l'info ou bien les intitulés des colonnes ne sont pas clairs etc etc donc certaines personnes préfèrent utiliser iostat que les multiples vues Oracle.

Nous allons donc voir comment historiser les résultats de la commande iostat et rendre ceux-ci lisibles sous Oracle. Mais bien sur vous pouvez historiser n'importe quelle commande Linux selon votre besoin, même un ls -l sur un répertoire Oracle pour voir si des fichiers y sont régulièrement créés par exemple.
 


 

Points d'attention
N/A.
 


 
Base de tests
Une base Oracle 18.


 
Exemples
============================================================================================
Créer le job lançant la commande Linux

============================================================================================
L'objectif va être de lancer la commande iostat toutes les 10 secondes via un job Oracle, de stocker le résultat dans un fichier texte puis de lire ce fichier non pas sous Linux mais directement depuis Oracle via une table externe; ceci pour le cas où on n'aurait pas la possibilité de se connecter sur le serveur Linux. Ce job n'a pas de date de fin, pour l'arrêter il faudra faire un STOP ou un DROP du job, et ce dans un autre terminal Linux.


Gestion du job
Sous SQL*Plus il faut lancer les commandes suivantes (je fais un test ici sur seulement 4 périodes de dix secondes).

On crée un job de type EXECUTABLE pour utiliser une commande du système d'exploitation : pour cela on lancera le shell /bin/sh avec deux arguments.
     SQL> exec DBMS_SCHEDULER.CREATE_JOB(job_name => 'JOB_IOSTAT',job_type => 'EXECUTABLE',job_action => '/bin/sh',number_of_arguments => 2);

L'argument -c permet de lancer une nouvelle instance du shell Linux où s'exécutera la commande iostat.
     SQL> exec DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE (job_name => 'JOB_IOSTAT',argument_position => 1,argument_value => '-c');

On saisi la commande Linux à lancer avec ses différents paramètres. Le résultat est stocké dans un fichier du répertoire /tmp pour être sur d'avoir les droits en lecture/écriture.
     SQL> exec DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE (job_name => 'JOB_IOSTAT',argument_position => 2,argument_value => 'iostat -d -x -t 10 4 >>/tmp/iostat_oracle.txt');

Et on lance le job! Attention, Oracle ne vous rendra la main que lorsque le job sera terminé, soit après 40 secondes dans notre test.
     SQL> exec DBMS_SCHEDULER.RUN_JOB(job_name => 'JOB_IOSTAT');

Pour arrêter le job, il faut lancer la commande
     SQL> exec DBMS_SCHEDULER.STOP_JOB(job_name => 'JOB_IOSTAT');

Pour dropper le job, il faut lancer la commande  
     SQL> exec DBMS_SCHEDULER.DROP_JOB(job_name => 'JOB_IOSTAT');

Si on regarde la vue des jobs, aucune erreur suite au lancement.
     SQL> select REQ_START_DATE, STATUS, ERROR#, ADDITIONAL_INFO from ALL_SCHEDULER_JOB_RUN_DETAILS where job_name = 'JOB_IOSTAT' and status <> 'SUCCEEDED' order by REQ_START_DATE;
     no rows selected


Contenu du fichier
Exactement ce que l'on veut : quatre fois l'exécution de la commande iostat avec les bons paramètres, notamment la date de lancement pour avoir un historique.

Les colonnes qui m'intéressent sont les moyennes pour la lecture et l'écriture de données : 
- r_await : the average time (in milliseconds) for read requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.
- w_await : the average time (in milliseconds) for write requests issued to the device to be served.

     SQL> ! more /tmp/iostat_oracle.txt
     Linux 4.1.12-61.1.27.el7uek.x86_64 (vbgeneric)     10/04/19     _x86_64_    (1 CPU)

10/04/19 10:21:03
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.07    0.68    0.37    17.99     3.29    40.39     0.04   42.46   20.44   83.12   5.15   0.54
sdb               0.02     0.87    1.17    3.46    47.86    27.67    32.59     0.03    6.32   22.09    0.97   1.78   0.83
dm-0              0.00     0.00    0.66    0.39    16.66     3.20    37.95     0.06   52.83   19.79  108.73   5.16   0.54
dm-1              0.00     0.00    0.01    0.00     0.05     0.00    16.95     0.00    3.14    3.14    0.00   2.97   0.00

10/04/19 10:21:13
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sdb               0.00     1.95    0.00    3.07     0.00    26.64    17.33     0.00    0.23    0.00    0.23   0.23   0.07
dm-0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00

10/04/19 10:21:23
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sdb               0.00     0.41    0.00    1.44     0.00    12.36    17.14     0.00    0.29    0.00    0.29   0.29   0.04
dm-0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00

10/04/19 10:21:33
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.10    0.00    1.65     0.00     7.95     9.62     0.00    0.94    0.00    0.94   0.19   0.03
sdb               0.00     1.24    0.21    5.68    81.82    45.04    43.09     0.02    3.35   88.00    0.27   1.79   1.05
dm-0              0.00     0.00    0.00    1.65     0.00     7.95     9.62     0.00    0.94    0.00    0.94   0.19   0.03
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00

============================================================================================
Lecture via une table externe

============================================================================================
Création de la table externe
Nous créons maintenant une table externe basique, sans découpage en colonne. L'objectif est de pouvoir lire le fichier depuis Oracle, pas de faire des requêtes sur telle ou telle colonne.

On  crée un directory sur le réperoire /tmp puis la table externe avec ses paramètres spécifiques.
     SQL> CREATE DIRECTORY tmp AS '/tmp';
     Directory created.

     SQL> CREATE TABLE SYSTEM.tmp_iostat
     (
         ligne VARCHAR2(4000) -- chaque ligne du fichier fait certainement moins de 4000 caractères
     )
         ORGANIZATION EXTERNAL -- c'est une table externe
         (
           TYPE ORACLE_LOADER
           DEFAULT DIRECTORY TMP -- répertoire Linux où est le fichier à lire
           ACCESS PARAMETERS
           (
             records delimited by newline -- je crée un enregistrement dans ma table par ligne du fichier
             badfile TMP:'iostat_oracle_table_externe.bad' -- fichier des enregistrements posant problème dans le répertoire TMP
             logfile TMP:'iostat_oracle_table_externe.log' -- fichier de log dans le répertoire TMP
             fields terminated by eof
           )
           LOCATION ('iostat_oracle.txt') -- le nom du fichier à lire
         )
         PARALLEL
         REJECT LIMIT UNLIMITED ;
Table created.


Lecture de la table
Aïe, problème quand on veut lire le fichier via la table...
     SQL> select * from SYSTEM.tmp_iostat;
     select * from SYSTEM.tmp_iostat
     *
     ERROR at line 1:
     ORA-29913: error in executing ODCIEXTTABLEOPEN callout
     ORA-29400: data cartridge error
     KUP-00554: error encountered while parsing access parameters
     KUP-01005: syntax error: found "minussign": expecting one of: "badfile,
     byteordermark, characterset, column, data, delimited, discardfile, dnfs_enable,
     dnfs_disable, disable_directory_link_check, field, fields, fixed, io_options,
     load, logfile, language, nodiscardfile, nobadfile, nologfile, date_cache,
     dnfs_readbuffers, preprocessor, readsize, string, skip, territory, variable,
     xmltag"
     KUP-01007: at line 1 column 30

La table existe bien, le problème est donc ailleurs.
     SQL> desc SYSTEM.tmp_iostat
     Name                       Null?    Type
     ----------------------------------------- ------
     LIGNE                            VARCHAR2(4000)

Le problème est peut-être lié au fait que j'utilise une VM Oracle et que le fichier créé n'appartient à personne... à nobody en fait!
     SQL> ! ls -l /tmp/iostat_oracle.txt
     -rw-r--r-- 1 nobody nobody 5184 Oct  4 10:21 /tmp/iostat_oracle.txt

Je change le owner après m'être connecté comme root.
     [root@vbgeneric ~]# chown oracle /tmp/iostat_oracle.txt

     [root@vbgeneric ~]# ls -l /tmp/iostat_oracle.txt
     -rw-r--r-- 1 oracle nobody 5184 Oct  4 10:21 /tmp/iostat_oracle.txt

Et zut, encore le même problème...
     SQL> select * from SYSTEM.tmp_iostat;
     select * from SYSTEM.tmp_iostat
                     *
     ERROR at line 1:
     ORA-29913: error in executing ODCIEXTTABLEOPEN callout
     ORA-29400: data cartridge error
     KUP-00554: error encountered while parsing access parameters
     KUP-01005: syntax error: found "minussign": expecting one of: "badfile,
     byteordermark, characterset, column, data, delimited, discardfile, dnfs_enable,
     dnfs_disable, disable_directory_link_check, field, fields, fixed, io_options,
     load, logfile, language, nodiscardfile, nobadfile, nologfile, date_cache,
     dnfs_readbuffers, preprocessor, readsize, string, skip, territory, variable,
     xmltag"
     KUP-01007: at line 1 column 30

Recommençons! Je droppe la table, je la recrée mais dans le schéma courant (HR) et je remplace les données sous Linux par un texte tout simple puisque le message d'erreur parlait d'un problème de signe moins (minus).
     SQL> drop table SYSTEM.tmp_iostat;
     Table dropped.

     SQL> CREATE TABLE tmp_iostat
     (
         ligne VARCHAR2(4000)
     )
         ORGANIZATION EXTERNAL
         (
           TYPE ORACLE_LOADER
           DEFAULT DIRECTORY TMP
           ACCESS PARAMETERS
           (
             records delimited by newline
             badfile TMP:'iostat_oracle_table_externe.bad'
             logfile TMP:'iostat_oracle_table_externe.log'
             fields terminated by eof
           )
           LOCATION ('iostat_oracle.txt')
         )
         PARALLEL
         REJECT LIMIT UNLIMITED ;
     Table created.

Et voilà, cette fois c'est OK.
     SQL> select * from tmp_iostat;
     LIGNE
     --------------------------------------------------------------------------------
     test

Je relance la création du job avec la commande iostat et cette fois le SELECT est tout bon :-)
     SQL> select * from tmp_iostat;
LIGNE
----------------------------------------------------------------------------------------------------------------------
Linux 4.1.12-61.1.27.el7uek.x86_64 (vbgeneric)     10/04/19     _x86_64_    (1 CPU)
10/04/19 10:53:14
Device:     rrqm/s     wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda          0.00       0.07    0.63    0.37    16.85     3.17    40.05     0.04   41.43   20.40   77.93   5.04   0.50
sdb          0.02       0.87    1.11    3.45    45.33    27.47    31.96     0.03    6.01   21.87    0.93   1.72   0.78
dm-0          0.00       0.00    0.61    0.38    15.62     3.09    37.68     0.05   51.54   19.74  102.31   5.05   0.50
dm-1          0.00       0.00    0.01    0.00     0.04     0.00    16.95     0.00    3.14    3.14    0.00   2.97   0.00
10/04/19 10:53:24
Device:     rrqm/s     wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda          0.00       0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sdb          0.00       1.55    0.00    2.58     0.00    21.47    16.64     0.00    0.20    0.00    0.20   0.20   0.05
dm-0          0.00       0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-1          0.00       0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
10/04/19 10:53:34
Device:     rrqm/s     wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda          0.00       0.10    0.00    0.52     0.00     4.96    19.20     0.00    0.00    0.00    0.00   0.00   0.00
sdb          0.00       1.14    0.00   12.62     0.00    72.80    11.54     0.01    0.79    0.00    0.79   0.78   0.98
dm-0          0.00       0.00    0.00    0.52     0.00     4.96    19.20     0.00    0.00    0.00    0.00   0.00   0.00
dm-1          0.00       0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
10/04/19 10:53:44
Device:     rrqm/s     wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda          0.00       0.00    0.00    0.10     0.00     0.41     8.00     0.00    0.00    0.00    0.00   0.00   0.00
sdb          0.00       0.52    0.10    2.28     0.41    17.84    15.30     0.00    0.22    0.00    0.23   0.22   0.05
dm-0          0.00       0.00    0.00    0.10     0.00     0.41     8.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-1          0.00       0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00

25 rows selected.



Publicité
Publicité
Commentaires
Blog d'un DBA sur le SGBD Oracle et SQL
Publicité
Archives
Blog d'un DBA sur le SGBD Oracle et SQL
  • Blog d'un administrateur de bases de données Oracle sur le SGBD Oracle et sur les langages SQL et PL/SQL. Mon objectif est de vous faire découvrir des subtilités de ce logiciel, des astuces, voir même des surprises :-)
  • Accueil du blog
  • Créer un blog avec CanalBlog
Visiteurs
Depuis la création 339 331
Publicité