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

mercredi 11 novembre 2015

Tables temporaires

Jusqu’à la 11g, les tables temporaires en mode commit preserve rows sont à manier avec précaution. Si vous vous contentez de les utiliser pour stocker quelques calculs intermédiaires, vous n’aurez pas de problèmes car un full scan fera bien l’affaire.

En revanche, si des tables temporaires sont utilisées dans un batch suite par exemple à une migration de Sybase vers Oracle, vous risquez alors de faire face à des problèmes de performance une fois une certaine volumétrie atteinte.

En effet, si une même requête au sens sql_id est utilisée plusieurs fois, un dynamic sampling souvent insuffisant sera utilisé pour collecter quelques statistiques afin d’élaborer un plan d’exécution lors de la création du curseur dans le shared pool. De plus, si cette requête est utilisée plusieurs fois par d’autres sessions, ce même plan d’exécution sera appliqué alors que le jeu de données risque d’être fort différent en volumétrie et en nature des données.

Pour éviter des performances dégradées et aléatoires, deux solutions peuvent être mises en oeuvre :
1)  Utilisation d’un hint pour forcer le dynamic sampling à 6, voire plus si nécessaire ;
2) Forcer le hard parsing à chaque exécution de requête (http://ahmedaangour.blogspot.fr/2011/07/forcer-un-hard-parse.html) pour obtenir un plan d’exécution adapté à chaque jeu de données.

A partir de la 12c, la possibilité de collecter des statistiques au niveau de chaque session (https://oracle-base.com/articles/12c/session-private-statistics-for-global-temporary-tables-12cr1) change la donne.

vendredi 16 janvier 2015

Histogramme: comment les décrypter

Un histogramme sur une colonne peut être utile si cette colonne a une distribution non uniforme de ses valeurs; on parle alors de skew distribution. Jusqu'à la 12c, il existait deux types d'histogramme: les histogrammes de type fréquence et les histogrammes de type height-balanced. Les premiers, sorte de diagramme en bâton, sont précis, mais ils sont limités à 256 valeurs. Les seconds, se basant sur un algorithme d'identification de valeurs populaires,  permettent de traiter les autres cas, mais ils peuvent être amenés à être imprécis pour certains types de distribution. Lorsque cela se produit, soit on supprime l'histogramme, soit on le fake si les données ont une certaine stabilité dans le temps.

Mais, avant de mettre en défaut un histogramme, il est utile de pouvoir le décrypter ( ! ) afin de vérifier sa validité. En effet, la lecture d'un histogramme est parfois tout sauf une chose aisée. Afin de vous aider dans cette tâche, j'ai chargé dans Google Drive un fichier exemple ( /oracle/histogram/etude_histogram.sql ). Sur ce point, un grand merci à J.Lewis, comme d'habitude ...

Depuis la 12c, on peut désormais avoir 2048 valeurs dans un histogramme de type fréquence. De plus, deux nouveaux types d'histogramme sont apparus: les histogrammes de type top frequency et les histogrammes hybrides.  Les premiers sont très intéressants car ce sont des histogrammes de type fréquence qui regroupe les top-n valeurs d'une colonne, ce qui est souvent représentatif de la réalité ( penser à une distribution de Zipfian pour simuler ce genre de colonne ). Les seconds sont une amélioration des histogrammes de type height-balanced.

Si vous souhaitez avoir plus d'infos sur ce sujet et plus généralement sur les nouveautés de la 12c au niveau de l'optimiseur: /oracle/paris_oracle_meetup/The Query Optimizer in Oracle Database 12c - What's new ? ( written by C.Antognini ).

Jusqu'à maintenant, je vous ai parlé d'histogramme sur une colonne. Ce type d'outil reste valide tant que les valeurs des colonnes sont indépendantes les unes des autres. Mais quand cela n'est plus le cas, l'optimiseur ne peut plus estimer correctement la sélectivité d'une clause where comportant par exemple deux colonnes dépendantes l'une de l'autre. Pour pallier ce défaut, Oracle a introduit en 11g la notion d'extended statistics.

Oracle implémente cette notion en créant une colonne virtuelle correspondant à la concaténation des colonnes ciblées, puis calcule les statistiques sur la colonne virtuelle: on peut alors obtenir un histogramme sur plusieurs colonnes. La colonne virtuelle est définie via la fonction sys_op_combined_hash.

Enfin, pour finir, SVP, ne supprimer pas les histogrammes d'une base sans raison !


mardi 29 octobre 2013

Histogramme

Une série d'articles intéressants sur les limites des histogrammes : http://allthingsoracle.com/histograms-part-3-when/

vendredi 26 octobre 2012

Result caching

Dernièrement, j'ai testé cette fonctionnalité sur une instance 11.2.0.2. Il s'agit d'un cache supplémentaire de données situé dans le shared pool; il est partageable par toutes les sessions.

Lorsqu'une requête contient le hint result_cache ou depuis la 11.2, si on utilise l'instruction alter table nom_table  result_cache ( mode force ), le résultat des requêtes est placé dans ce cache.  Si une requête est bindée, il existe une entrée pour chaque n-uplet de bind variables. Pour la deuxième option ( alter table ), il peut être utile d'augmenter la taille initiale du cache: alter system set result_cache_max_size = x m scope=both.

A chaque fois qu'une table référencée dans une requête est modifiée, l'entrée correspondante dans le cache est invalidée et la requête s'exécute alors classiquement avant de créer une nouvelle entrée dans le cache avec les données mises à jour ( colonne build_time ). Afin d'optimiser le contenu du cache, la vue v$result_cache_objects permet de répérer les requêtes fréquemment invalidées ansi que celles bénéficiant d'un hit intéressant ( colonne scan_count ).

La vue v$result_cache_dependency permet d'identifier les tables utilisées par une entrée du cache. Puis, on peut trouver la dernière modification de type DML de la table via cette requête: select scn_to_timestamp(max(ora_rowscn) from nom_table. D'autres méthodes sont disponibles: utilisation de la fonction d'audit, alter table monitoring, v$sql, ...

Sur quelques exemples testés, le gain en performance est appréciable. Cependant, on peut regretter qu'il soit difficilement évaluable lorsqu'on décide d'associer une table au cache. En effet, la vue v$resultat_cache_objects stocke l'identifiant du cache ( colonne cache_id ) apparaissant dans le plan d'exécution et non l'identifiant de requête. Et la vue ne conserve que les 128 premiers caractères de chaque requête ...

La vue v$result_cache_statistics permet d'avoir une vue générale du fonctionnement du cache ( nombre d'entrées invalidées, nombre de hits, ... ) et de s'assurer que la taille du cache est correcte ( colonne Delete Count Valid ). On peut aussi obtenir un rapport de la mémoire du cache via la procédure dbms_result_cache.memory_report.

Pour finir, il existe le même type de fonctionnalité pour les résultats des fonctions PL/SQL.

vendredi 23 septembre 2011

Real-time sql monitoring

Ce nouvel outil de la 11g permet de synthétiser un ensemble de vues du catalogue dans une animation flash. Il est directement utilisable via sql developer ( Tools -> Monitor SQL ).

Il peut être utilisé pour étudier les requêtes de plus de 5 s et surtout pour les requêtes parallèles ( le modèle producteur-consommateur est plus compréhensible au niveau du plan d'exécution et un onglet décrit l'activité de chaque serveur parallèle ). De plus, il est visualisable dès le début de l'exécution de la requête et se complète au fur et à mesure du traitement de la requête.

Les nouvelles vues liées à cet outil sont v$sql_monitor et v$sql_plan_monitor.

Illustration: un exemple de requête parallèle.

Lors d'un access full à la table t1_parallel, on note des waits events de type direct path read et non des waits events de type db file scattered read, comportement usuel pour une requête en mode parallèle. Cette méthode est également plus fréquemment utilisée en 11g pour les requêtes en mode série: http://blog.easyteam.fr/2011/02/28/effet-cache-de-la-database-11g-direct-io/.

D'autres exemples, ainsi que le test case, sont disponibles ici.

Ce dernier s'inspire des deux articles écrits par arkzoyd sur le nouveau fonctionnement de la parallélisation des requêtes en 11.2. Il permet ainsi à une requête en mode parallèle de suspendre son traitement si elle ne dispose pas des ressources suffisantes plutôt que de s'exécuter en mode série. Une fois les serveurs parallèles de nouveau disponibles, la requête est lancée en mode parallèle.

jeudi 6 janvier 2011

Event trace 10053

L'event trace 10053 permet de comprendre comment l'optimiseur a créé le plan d'exécution d'une requête. A chaque noeud du plan, on peut voir comment l'optimiseur a pris une décision, par exemple le choix d'un index range scan plutôt qu'un table full scan.

Pour étudier plus facilement ce type de trace, un viewer compatible 10g et 11g est disponible à cette adresse:
http://jonathanlewis.wordpress.com/2010/04/30/10053-viewer/.

Pour tenter de déchiffrer l'event trace 10053, vous pouvez consulter:
- le chapitre 14 du livre Cost Based Oracle Fundamentals écrit par J.Lewis;
- la note Metalink 338137.1: Analyzing 10053 Trace Files;
- la présentation de W.Breitling “A Look under the Hood of CBO – the 10053 Event”.

En 11g, on peut capturer une trace 10053 pour une requête appartenant à un package PL/SQL.

jeudi 1 juillet 2010

Analyse d'une requête

Ce lien donne une liste d'outils permettant d'effectuer un diagnostic sur une requête SQL: http://oracle-randolf.blogspot.com/2009/02/basic-sql-statement-performance.html.

dimanche 2 mai 2010

SPM ( SQL Plan Management )

Quelques liens:
- http://optimizermagic.blogspot.com/search/label/sql plan management
- http://www.oracle-base.com/articles/10g/AutomaticSQLTuning10g.php#sql_tuning_sets
- http://www.oracle-base.com/articles/11g/AutomaticSqlTuning_11gR1.php  

Ce nouveau concept de la 11g permet de figer les plans d'exécution comme les stored outlines. De plus, il collecte pour une requête un meilleur plan d'exécution si l'optimiseur  a trouvé un chemin d'accès plus performant pour récupérer les données. De cette manière, on est désormais à l'abri des changements brusques des plans d'exécution ( qui n'a pas connu cela sur une base de production ? ), tout en pouvant améliorer les temps d'exécution des requêtes de manière contrôlée.

dimanche 28 mars 2010

Cardinality feedback

Quelques liens:
- http://dioncho.wordpress.com/2009/12/17/trivial-research-on-the-cardinality-feedback-on-11gr2/
- http://wedostreams.blogspot.com/2009/12/hidden-undocumented-and-adaptive-cursor.html 
- http://jonathanlewis.wordpress.com/2009/12/16/adaptive-optimisation/ 

En se connectant en tant que SYS, vous pouvez visualiser les caractéristiques d'un paramètre caché de la manière suivante:
select ksppinm as param,
       y.ksppstvl as ses_val,
       z.ksppstvl as inst_val,
       decode(bitand(ksppiflg/256,1),1,'TRUE','FALSE') as ses_modif,
       decode(bitand(ksppiflg/65536,3),1, 'IMMEDIATE',2,'DEFERRED', 3,'IMMEDIATE','FALSE') as system_modif,
       ksppdesc as descr
from x$ksppi x, x$ksppcv y, x$ksppsv z
where x.indx = y.indx
  and x.indx = z.indx
  and ksppinm like '\_%' escape '\'
order by ksppinm;

dimanche 14 février 2010

Extended cursor sharing

Jusqu'à la version 11g, le mécanisme dit de peek bind variable posait parfois des problèmes lorsque la sélectivité des différentes valeurs des bind variables était différente. Désormais, le mécanisme dit d'extended cursor sharing permet de corriger ce défaut après quelques exécutions.

Pour illustrer ce nouveau mécanisme, on crée une nouvelle table:
create table adapt_curs_share (
   idf number,
  date_ins date,
  chaine varchar2(80),
  nombre number not null )
tablespace 'nom_tablespace';

create sequence seq_adapt_curs_share
increment by 1
start with 1
;  


create index idx_adapt_curs_share_nombre on adapt_curs_share(nombre) tablespace 'nom_tablespace';

On l'alimente de la manière suivante:
begin
   for i in 1..100000
   loop
      insert into adapt_curs_share
      (idf, date_ins, chaine, nombre)
      values
      (seq_adapt_curs_share.nextval, sysdate, dbms_random.string('P', 80), abs(mod(dbms_random.random(),1000 )));
   end loop;
  
   commit;
end;

On calcule les statistiques et on nettoie le shared pool :
dbms_stats.gather_table_stats(
ownname => 'schéma',
tabname => 'adapt_curs_share',
method_opt=>'for all columns size 1',
cascade => true);

alter system flush shared pool;

Avant d'ouvrir la session sous DOS,  il est souhaitable de positionner le bon jeu de caractères:

On positionne le niveau le plus élevé pour les statistiques afin de recueillir plus d'informations au niveau des plans d'exécution:
alter session set statistics_level = all;

Une fois la configuration mise en place, on exécute sous sqlplus les instructions suivantes:
variable n number
exec :n := 999
select count(idf) from adapt_curs_share where nombre < :n; ( résultat: 99893 )
exec :n :=3
select count(idf) from adapt_curs_share where nombre < :n; ( résultat: 308 )

Le plan d'exécution de la requête est le suivant: 

Le contenu de la bind variable est 999, la valeur utilisée par la première exécution de la requête. Etant donné la faible sélectivité de la requête, un access full est effectué sur la table adapt_curs_share. La deuxième requête, bien que très sélective, va utiliser le même plan d'exécution, d'où l'idée d'améliorer le mécanisme de peek bind variable via le concept d'extended cursor sharing.

Dans la vue v$sql, les colonnes is_bind_sensitive ( utilisation du peek bind variable ), is_bind_aware ( utilisation de l'extended cursor sharing ) et is_shareable ( curseur partagé ou non ) donnent pour ce curseur le triplet (Y, N, Y).

Suite à ces deux premières exécutions, on effectue les opérations suivantes:

exec :n :=3
select count(idf) from adapt_curs_share where nombre < :n;
exec :n :=999
select count(idf) from adapt_curs_share where nombre < :n;

Pour le curseur 9fyq0vr5fh4dn, on observe désormais dans la vue v$sql trois fils au lieu d'un. Le fils 0 a exécuté les deux premières requêtes. Il utilise le mécanisme du peek bind variable, mais pas celui de l'extended cursor sharing et il n'est plus partageable.

Le fils 1 a exécuté la troisième requête ( bind variable = 3 ). Il utilise le peek bind variable, l'extended cursor sharing et il est partageable. Son plan d'exécution est le suivant;

Le fils 2 a exécuté la quatrième requête ( bind variable = 999 ). Il utilise le peek bind variable, l'extended cursor sharing et il est partageable. Son plan d'exécution est le suivant;

Si on exécute de nouveau la requête avec les deux valeurs 3 et 999, seuls les fils 1 et 2 sont utilisés.

Pour aller plus loin sur ce nouveau concept lié à la 11g, vous pouvez aussi consulter les vues v$sql_cs_statistics, v$sql_cs_selectivity et v$sql_cs_histogram.

mardi 12 janvier 2010

Plan d'exécution ( partie 2 )

Une fois une requête exécutée, vous pouvez visualiser son plan d'exécution contenu dans la library cache, une partie du shared pool ( SGA ) via la commande suivante: select * from table(dbms_xplan.display_cursor('id_requete', 0));.

L'identifiant de requête ( sql_id ) est une chaîne de caractères ( exemple: dqbzw72kjmvhb ). On peut le retrouver à l'aide de la vue v$sql ( colonnes sql_text et sql_fulltext pour une recherche sur le texte de la requête, colonnes last_load_time et last_active_time pour la première ou dernière exécution ).

Pour utiliser le package dbms_xplan, il faut pouvoir lire les vues du catalogue. Pour ce faire, l'instruction est la suivante: grant select_catalog_role to 'utilisateur';.

Pour information, la fonction display_cursor est une encapsulation des vues v$sql_plan et v$sql_plan_statistics_all.

mardi 1 décembre 2009

Plan d'exécution ( partie 1 )

Pour estimer un plan d'exécution, vous pouvez utiliser la commande set autotrace on sous sqlplus. Vous obtenez en plus les statistiques de la requête ( consistent gets, physical reads, sorts ... ). Le document de référence d'Oracle décrit ces différentes statistiques.

Une autre méthode consiste à exécuter les deux commandes suivantes:
- explain plan for 'requête';
- select * from table(dbms_xplan.display);

Pour plus d'infos: http://www.oracle-base.com/articles/9i/DBMS_XPLAN.php

mardi 17 novembre 2009

Calcul des statistiques

En 10g, le calcul automatique des statistiques est réalisé par un job nommé GATHER_STATS_JOB. Par défaut, ce job est lancé tous les jours entre 22h et 6h. Il permet la mise à jour des statistiques des objets ( tables, index ) ayant subi de nombreuses modifications. Ce traitement est similaire à la procédure DBMS_STATS.GATHER_DATABASE_STATS utilisant l'option GATHER AUTO. Il peut être visualisé dans la vue dba_scheduler_jobs.

En 11g, il est désormais considéré comme une tâche automatique ( autotask ). Sa définition est précisée dans la vue dba_autotask_task. Pour suivre ses exécutions, on peut utiliser cette requête: select * from dba_scheduler_job_log where job_name like 'ORA$AT_OS_OPT_SY_%' order by log_date desc.

La vue dba_tab_modifications permet de voir le nombre de modifications effectuées sur une table depuis le dernier calcul de statistiques.