Statistiques de parsing des requêtes, par Jonathan Lewis

Cet article est la traduction d’un article de Jonathan Lewis publié sur son blog. L’article original en anglais se trouve ici.

Jonathan Lewis donne la définition des statistiques qui concernent les parsing des requêtes : session cursor cache hits parse count (total) parse count (hard) execute count
Vous trouverez aussi quelques mots sur le parsing ici

Voici un extrait d’un rapport que j’ai fait récemment sur l’activité d’une session. Cherchez l’erreur:

Name                           Value
----                           -----
session cursor cache hits          3
parse count (total)                5
parse count (hard)                31
execute count                     35

Il n’y a pas d’astuce particulière pour avoir ce résultat, même si l’activité de la base de données est un peu particulière, et je n’ai pas trafiqué les chiffres. Alors comment se fait-il qu’on ait plus de ‘hard parse‘ que de ‘parses‘ ?

Je n’aime pas du tout le terme ‘hard parse‘, je ne suis pas très enthousiaste à propos de ‘soft parse‘, et je suis irrité par la confusion fréquente où l’on ne distingue pas les ‘parse calls‘ et le parsing.

Les statistiques que j’ai cité, et qui semblent bizarres, s’expliquent facilement.
parse count (hard)‘ devrait plutôt s’appeler ‘optimizations‘, et ces optimisations d’une requête peuvent être nécessaires pour l’appel de l’exécution de la requête (‘execute call‘) aussi bien que pour l’appel de parse de la requête (‘parse call‘).

Les ‘parse count (total)‘ devraient plutôt être appelés ‘parse calls‘, et il y a des ‘parse calls‘ qui ne nécessitent aucun parsing, lorsque la requête se trouve dans le cache des curseurs de la session (session cursor cache).

Tout ce que j’ai fait pour cette démonstration se trouve dans ce simple bloc pl/sql:

declare
  m_n  number;
begin
  for i in 1..30 loop
    select count(*) into m_n from t1;
    dbms_lock.sleep(1);
  end loop;
end;
/

Mais il faut noter l’appel à dbms_lock.sleep. Lorsque ce bloc a été exécuté, j’ai fait un truncate de la table t1 toutes les secondes à partir d’une autre session.

Parce que je suis en pl/sql, le select est automatiquement retenu dans le cache des curseurs, il n’est parsé qu’une fois et exécuté 30 fois, donc il n’y a pas 30 ‘parse calls‘, mais un seul ‘parse call‘ et 30 ‘execute calls

Mais parce qu’il y a les truncate à chaque fois dans l’autre session, le plan d’exécution qui été généré pour la requête au moment du ‘parse call‘ (qui a parsé, optimisé et gardé le curseur ouvert) s’est trouvé invalidé à peu près à chaque exécution.

Alors, pratiquement à chaque exécution, il a fallu réoptimiser la requếte à nouveau. Si vous vérifiez V$SQL vous verrez que le curseur fils a subi 30 invalidations et loads.

En résumé:

‘parse count (total)’ est le nombre de demande de parse (‘parse calls‘). Si la requête n’a jamais été vue avant, cela entraîne un parse et une optimisation. S’il elle a déjà été vue, celà entraine une recherche dans le ‘library cache‘ (c’est la manière de savoir qu’elle a déjà été vue) et peut entraîner un ‘cursor authentication‘. Si elle a déjà été vue, et authentifiée, et qu’elle a une entrée dans le cache de session (session cursor cache) alors l’appel de parsing ‘parse call‘ ne fait quasiment rien, parce qu’il n’y a même pas besoin de faire de recherche dans le library cache vu qu’il a été implicitement retenu.

‘parse count (hard)’ est le nombre d’optimisation qui ont eu lieu. L’optimisation peut être la conséquence d’un ‘parse call‘ ou bien d’un ‘execute call‘. Même un curseur qui a été retenu peut avoir eu son plan d’exécution invalidé, ce qui entraîne une réoptimisation à la prochaine exécution.

‘session cursor cache hits’ est le nombre de fois où un ‘parse call’ n’a eu quasiment rien à faire parce que la requête a été retenue automatiquement lorsque la couche OCI a dérecté une utilisation répétée de la requête.

Ajout:

Il y a une autre statistique dont je n’ai pas parlé. Elle n’apparaît pas souvent dans un rapport statspack, mais pour être complet, je dois le mentionner car cela peut embrouiller les choses.

Si vous exécutez une requête qui a une erreur au parsing (comme select ssdate from dual) alors l’optimiseur retourne l’erreur qui correspond (ORA-00904 dans cet exemple). La statistique ‘parse count (failures)‘ est incrémentée, tout comme ‘parse count (hard)‘.

Mais malheureusement, ‘parse count (total)‘ peut ne pas changer. C’était le cas lorsque j’ai essayé à partir de SQL*Plus en version 9.2.0.8, mais elle a été incrémentée lorsque j’ai essayé en 10.2.0.3. C’est donc une autre cas où l’on peut avoir plus de ‘hard parses‘ que de ‘parses‘.

Si vous voyez beaucoup de ‘parse count (failures)’, vous allez aussi voir les évènements de waits correspondants: ‘SQL*Net break/reset to client‘ (ou dblink, selon d’ou vient la requête).

Une réflexion au sujet de « Statistiques de parsing des requêtes, par Jonathan Lewis »

  1. Voici Deux précisions suites aux commentaires qu’il y a eu sur l’article original:

    Sur l’activité CPU liée à ces statistiques

    parse count(hard)‘ est lié à l’activité de l’optimiseur, ce qui veut dire de la consommation CPU et une plus forte activité sur le library cache, un possible contention sur les latches de shared pool et éventuellement les latches du cache du dictionnaire (row cache).

    Parse count(total)’ – ‘session cursor cache hit’ provoquent une certaine activité sur le library cache, et les latches de shared pool maus habituellement beaucoup moins d’activité CPU que les hard parses

    session cursor cache hit‘ laissent tranquille aussi bien la CPU que les latches.

    Mais bien sûr l’activité dépends aussi de la fréquence: il n’y a pas lieu de s’inquiéter de 10000 hard parses si on voir 50 millions de hits sur le session cursor cache.

    Sur le cache des curseurs en pl/sql (qui est différent du cache des curseurs de la session)

    Sur les versions d’Oracles inférieures à la 9.2.0.5, la taille du cache des curseurs du pl/sql se basait sur le paramètre open_cursors qui est souvent assez élevé.
    A partir de la 9.2.0.5, sa taille est basée sur session_cached_cursors qui est souvent plus petit.

Laisser un commentaire