Une matinée avec Jonathan Lewis

meetup_logo-500x370

Le groupe Paris Oracle Meetup, animé entr’autres par Gregory Guillou (plus connu sous le pseudonyme de ArKZoYD ) propose une rencontre avec Jonathan Lewis au matin du 27 mai à Nanterre. Ce dernier y parlera de performances des bases de données notamment au travers de l’interprétation des plans d’exécution et de l’écriture de SQL efficace et performant.

Jonathan Lewis est une référence du microcosme Oracle et est notamment auteur du célèbre Cost-based Oracle fundamentals.

Une bonne occasion de rejoindre le groupe Paris Oracle Meetup, de participer et de rencontrer d’autres passionnés des applications Oracle.

Dernier détail, et non des moindres, c’est en anglais, on s’en doutait mais ça va mieux en le disant certes, mais surtout ces meetups sont tous GRATUITS ce qui de nos jours est de plus en plus rare. Profitez !

Requêtage du vide 1/2

Je veux générer la consommation de temps de chaque SQL_ID sur une période donnée, j’utilise ASH (ou plus exactement v$active_session_history).

Je définis un certain nombre de variables pour rendre le code lisible et ré-utilisable.

  • deb: date de début de l’échantillonnage
  • fin: date de fin de l’échantillonnage
  • nbsec: nombre de seconde d’aggrégation
  • nbVals: nombre de valeurs à générer dans l’intervalle d’échantillonnage
  • DBID: commenté, pourrait serveir à travailler avec DBA_HIST_ACTIVE_SESS_HISTORY
var deb varchar2(25)
var fin varchar2(25)
var frmt varchar2(25)
var nbsec number
var nbVals number
-- var dbid number

Je positionne les valeurs des variables, je veux échantillonner sur les 10 dernières minutes

-- exec select dbid into :dbid from v$database
exec :frmt :=  'YYYYMMDDHH24MISS' 
exec select to_char(trunc( sysdate - interval '10' minute , 'MI'), :frmt ), to_char( trunc (sysdate, 'MI'), :frmt) into :deb, :fin from dual 
exec :nbsec := 60
exec :nbVals := 10

La requête est la suivante (et elle n’est forcément pas simple)

with dts as ( select  to_char( to_date(:deb, :frmt)  
                             + ( rownum/ ( 60 *60 *24 ) ) * :nbsec  
                             , :frmt ) dt 
              from dual
              connect by rownum <= :nbVals ) ,
-- ----------------------------------------------------------------------------------
    vals as ( select to_char(trunc(SAMPLE_TIME, 'MI'), :frmt) sample_time_min        
--                   , INSTANCE_NUMBER
--                   , DBID    
                   , SQL_ID 
                   , count(*) sec_per_min
--                   , count(*) * 10 sec_per_min
              from v$active_session_history 
--              from dba_hist_active_sess_history 
              where SAMPLE_TIME between to_date(:deb, :frmt) and to_date(:fin, :frmt)
--                and DBID = :dbid
              group by trunc(SAMPLE_TIME, 'MI')           
--                     , INSTANCE_NUMBER
--                     , DBID
                     , SQL_ID ),
-- ----------------------------------------------------------------------------------
  TOPSQL as ( select SQL_ID 
                   , sum(sec_per_min) pos
              from vals
              group by sql_id
              order by 2 desc, sql_id asc )
-- ----------------------------------------------------------------------------------
select dt
     , pos
     , sql_id
     , max(sec_per_min) sec_per_min
from ( Select distinct dt
            , sql_id
            , pos
            , case when dt=sample_time_min then sec_per_min 
                   else                         0           end  sec_per_min
       from dts, vals natural join TOPSQL )
group by dt, sql_id, pos
order by dt, sql_id, pos desc
/

Décorticons

  • La sous requête factorisée DTS :
    select  to_char( to_date(:deb, :frmt)  
                   + ( rownum/ ( 60 *60 *24 ) ) * :nbsec  
                   , :frmt ) dt 
    from dual
    connect by rownum <= :nbVals

    permet de générer les dates d’échantillons à retenir sous forme d’horodatage régulier.

  • La sous requête factorisée VALS (sans les commentaires qui permettent de travailler en mode historique)
    select to_char(trunc(SAMPLE_TIME, 'MI'), :frmt) sample_time_min           
         , SQL_ID 
         , count(*) sec_per_min
    from v$active_session_history 
    where SAMPLE_TIME between to_date(:deb, :frmt) and to_date(:fin, :frmt)
    group by trunc(SAMPLE_TIME, 'MI')           
           , SQL_ID 

    permet de relier les SQL_ID à leur charge

  • La sous requête factorisée TOPSQL
    select SQL_ID 
         , sum(sec_per_min) pos
    from vals
    group by sql_id
    order by 2 desc, sql_id asc )

    permet de calculer la charge globale de chaque SQL_ID et de les classer.

  • LA requête en tant que telle effectue le produit cartésien et ne retient que ce qui l’intéresse … C’est cher payé, mais ça donne le résultat escompté
    select dt
         , pos
         , sql_id
         , max(sec_per_min) sec_per_min
    from ( Select distinct dt
                , sql_id
                , pos
                , case when dt=sample_time_min then sec_per_min 
                       else                         0           end  sec_per_min
           from dts, vals natural join TOPSQL )
    group by dt, sql_id, pos
    order by dt, sql_id, pos desc

Le résultat avec un peu de présentation ( break on dt skip 1 ) :

DT                               POS SQL_ID        SEC_PER_MIN
------------------------- ---------- ------------- -----------
20150427211800                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v           0

20150427211900                    10 a1xgxtssv5rrp          10
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v           0

20150427212000                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v           0

20150427212100                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v           0

20150427212200                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v           0

20150427212300                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j          10
                                  10 gv1qak5fn39tk          10
                                  10 935y1fyshgm9v           0

20150427212400                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a          10
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v           0

20150427212500                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v           0

20150427212600                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v          10

20150427212700                    10 a1xgxtssv5rrp           0
                                  10 cyz7q9n00p23a           0
                                  10 d0v9kqgaysm5j           0
                                  10 gv1qak5fn39tk           0
                                  10 935y1fyshgm9v           0


50 lignes sélectionnées.

Certes il fallait y réfléchir un peu plus d’une demi seconde mais …

C’était pourtant pas si compliqué.

Ca reste pourtant très perfectible. On va la remettre sur le métier, histoire que ça ne soit pas du vite-fait mal-fait. Suite dans un prochain épisode.

(Dés)activer les taches automatiques

Sous Oracle on ne sait jamais si les tâches automatisées (autotasks en langue de Redwood Shores) sont actives ou non, lesquelles sont-elles et comment les activer ou les désactiver.

Lister les tâches automatiques actives

SELECT client_name, status
FROM dba_autotask_client
/
CLIENT_NAME                         STATUS
----------------------------------- ----------
auto optimizer stats collection     ENABLED
auto space advisor                  ENABLED
sql tuning advisor                  ENABLED

En désactiver une (en l’occurrence l’auto space advisor)

BEGIN
  dbms_auto_task_admin.disable('auto space advisor'
                              , NULL
                              , NULL);
END;
/

PL/SQL procedure successfully completed.

Pour vérifier

SELECT client_name, status
FROM dba_autotask_client
/
CLIENT_NAME                         STATUS
----------------------------------- ----------
auto optimizer stats collection     ENABLED
auto space advisor                  DISABLED
sql tuning advisor                  ENABLED

En activer une (en l’occurrence l’auto space advisor)

BEGIN
  dbms_auto_task_admin.enable( 'auto space advisor'
                              , NULL
                              , NULL);
END;
/

PL/SQL procedure successfully completed.

Vérifier

SELECT client_name, status
FROM dba_autotask_client
/
CLIENT_NAME                         STATUS
----------------------------------- ----------
auto optimizer stats collection     ENABLED
auto space advisor                  ENABLED
sql tuning advisor                  ENABLED

C’était pourtant pas si compliqué.

Générer un horodatage régulier

Problématique : on veut générer une suite de dates (surtout les heures, minutes et secondes) depuis en SQL pur

-- nombre de secondes
var nbsec number

-- nombre de valeurs souhaitées 
var nbVals number

-- On veut les 15 premiers multiples de 19 secondes
exec :nbsec := 19
exec :nbVals := 15 

-- La requête
select  to_char( trunc(sysdate-1) 
               + ( rownum/ ( 60 *60 *24 ) ) * :nbsec  
               , 'HH24:MI:SS' ) dt 
from dual
connect by rownum <= :nbVals ; 

Résultat

DT
--------
00:00:19
00:00:38
00:00:57
00:01:16
00:01:35
00:01:54
00:02:13
00:02:32
00:02:51
00:03:10
00:03:29
00:03:48
00:04:07
00:04:26
00:04:45

15 lignes sélectionnées.

C’était pourtant pas si compliqué.

Trouver les informations basiques d’une base Oracle à laquelle on est connecté

La requête

SELECT SYS.UTL_INADDR.get_host_address      IP_ADDR
     , SYS.UTL_INADDR.get_host_name         HOSTNAME
     , SYS.DBMS_UTILITY.CURRENT_INSTANCE    INST#
     , SYS.DBMS_UTILITY.port_string         OS
     , platform_id
     , platform_name
     , name
     , DB_UNIQUE_NAME
     , DBID
     , current_scn
     , decode ( parallel, 'YES', 'RAC', 'NOT RAC') IS_RAC
from v$database natural join v$instance;

Le résultat (splitté pour une meilleure lecture)

IP_ADDR                      HOSTNAME     INST# OS                   
---------------------------- ------------ ----- -------------------- 
fe80::4b5:7ff9:2cca:6ca8%3   ALPHAORIOJO      1 IBMPC/WIN_NT64-9.1.0 

PLATFORM_ID PLATFORM_NAME                  NAME       DB_UNIQUE_NAME 
----------- ------------------------------ ---------- -------------- 
         12 Microsoft Windows x86 64-bit   OJOTST     ojotst     

        DBID CURRENT_SCN IS_RAC
------------ ----------- --------
  2831882428     8207733 NOT RAC

C’était pourtant pas si compliqué !

Changer la fréquence et la rétention des snapshots AWR

La procédure à utiliser :

PROCEDURE MODIFY_SNAPSHOT_SETTINGS
 Argument Name                Type                    In/Out Default?
 ---------------------------- ----------------------- ------ --------
 RETENTION                    NUMBER                  IN     DEFAULT
 INTERVAL                     NUMBER                  IN     DEFAULT
 TOPNSQL                      NUMBER / VARCHAR2       IN     DEFAULT 
 DBID                         NUMBER                  IN     DEFAULT
  • RETENTION s’exprime en minutes (minimum 1 jour, maximum 100 ans)
  • INTERVAL s’exprime en minutes (minimum 10 minutes, maximum 1 an)

La commande (1 snap toutes les 15 minutes, 2 semaines de rétention):

begin
 dbms_workload_repository.modify_snapshot_settings( 60*24*14, 15 );
end;
/

C’était pourtant pas si compliqué !

Vérifier le dernier PSU appliqué PSU sur Oracle

La commande

select * from registry$history
/

Le résultat (11.2.0.4 PSU 1 )

ACTION_TIME                    ACTION                         
------------------------------ ------------------------------ 
29-JAN-15 10.38.47.581603 AM   APPLY                          

NAMESPACE                      VERSION     ID COMMENTS        
------------------------------ ---------- --- --------------- 
SERVER                         11.2.0.4     1 PSU 11.2.0.4.1  

BUNDLE_SERIES
------------------------------
PSU

C’était pourtant pas si compliqué !

Trouver les “lossy datas”

Vous avez ouvert une SR auprès du support Oracle(c) et l’analyste vous demande de trouver les “lossy datas”. sans plus d’explication que ça …

En fait ça se trouve avec CSSCAN

Il faut l’installer en tant que SYS sous SQLPLUS

set TERMOUT ON
set ECHO ON
spool csminst.log
drop user csmig cascade;
@?/rdbms/admin/csminst.sql

Et il faut le lancer avec une commande shell

csscan tochar=AL32UTF8 full=y

3 fichiers texte sont alors générés :

  • scan.out dans lequel est redirigée la sortie écran
  • scan.err dans lequel sont stockées les éventuelles erreurs de conversion
  • scan.txt qui stocke le rapport d’analyse

C’était pourtant pas si compliqué !

My Oracle Support - Créer un profile SQL avec des hints

Créer un profile SQL avec des hints

Créer un profile SQL avec des hints nécessite Tuning Pack mais est utile lorsqu’on fait face à un progiciel qui ne peut aller modifier son code. En effet, quel DBA n’est pas régulièrement confronté au problème suivant :

  • J’ai trouvé ce qui ne marche pas dans mon code SQL.
  • J’ai testé avec certains hints et ça a réglé le problème.
  • L’éditeur (ou le développeur) refuse de modifier son code pour implémenter les hints.

Il est possible depuis la version 10g de positionner des hints sur une requête sans aller modifier le code applicatif. La technique utilise le package dbms_sqltune et est donc soumise à licence.

(suite…)

Enregistrement automatique des services par PMON

  • Si votre base est bien installée.
  • Si votre serveur est bien configuré.
  • Si un service est créé dans la base (ok, il y en a toujours un).
  • Si le listener LISTENER est démarré sur le port 1521.
  • Et même si les paramètres local_listener ou remote_listener ne sont pas renseignés.

ALORS pmon enregistre le service auprès du listener.

Oui mais des fois, ça ne marche pas, et on ne sait pas trop pourquoi.
(suite…)