4 votes

Oracle - Comprendre l'indice no_index

J'essaie de comprendre comment no_index accélère réellement une requête et je n'ai pas réussi à trouver de documentation en ligne pour l'expliquer.

Par exemple, j'ai cette requête qui a été exécutée extrêmement lent

select  * 
    from    <tablename>
    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString' and
            Action_='_someAction_' and 
            Timestamp_ >= trunc(sysdate - 2)

Et l'un de nos DBA a pu l'accélérer de manière significative en faisant ceci

select  /*+ NO_INDEX(TAB_000000000019) */ * 
    from    <tablename>
    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString' and
            Action_='_someAction_' and 
            Timestamp_ >= trunc(sysdate - 2) 

Et je n'arrive pas à comprendre pourquoi ? J'aimerais comprendre pourquoi cela fonctionne afin de voir si je peux l'appliquer à une autre requête (cette fois-ci une jointure) pour l'accélérer car elle prend encore plus de temps à s'exécuter.

Merci !


** Mise à jour ** Voici ce que je sais de la table de l'exemple.

  • C'est une "table partitionnée".
  • TAB_000000000019 est la table et non une colonne.
  • field1 est indexé

9voto

skaffman Points 197885

L'optimiseur d'Oracle juge de la meilleure façon d'exécuter une requête, et pour ce faire, il utilise un grand nombre de statistiques recueillies sur les tables et les index. Sur la base de ces statistiques, il décide d'utiliser ou non un index, ou de se contenter d'un balayage de table, par exemple.

Il est essentiel que ces statistiques ne soient pas automatiquement mises à jour, car leur collecte peut être très coûteuse. Dans les cas où les statistiques sont no à jour, l'optimiseur peut prendre la "mauvaise" décision, et peut-être utiliser un index alors qu'il serait en fait plus rapide d'effectuer un balayage de table.

Si le DBA/développeur le sait, il peut donner des indications (ce qui est ce que le NO_INDEX est) à l'optimiseur, lui indiquant de ne pas utiliser un index donné parce qu'il est connu pour ralentir les choses, souvent en raison de statistiques périmées.

Dans votre exemple, TAB_000000000019 fera référence à un index ou à une table (je suppose qu'il s'agit d'un index, puisque cela ressemble à un nom généré automatiquement).

C'est un peu un art noir, pour être honnête, mais c'est l'essentiel, tel que je le comprends.

Clause de non-responsabilité : je ne suis pas un DBA, mais j'ai déjà travaillé dans ce domaine.

3voto

Dave Costa Points 25282

Si le champ 1 est le seul champ indexé, la requête originale a probablement effectué un balayage complet rapide sur cet index (c'est-à-dire qu'elle a lu chaque entrée de l'index et vérifié les conditions de filtrage sur le champ 1), puis a utilisé ces résultats pour trouver les lignes de la table et filtrer sur les autres conditions. Les conditions du champ 1 sont telles qu'un balayage unique de l'index ou un balayage de plage (c'est-à-dire la recherche de valeurs spécifiques ou de plages de valeurs dans l'index) ne serait pas possible.

Il est probable que l'optimiseur ait choisi ce chemin parce qu'il y a deux prédicats de filtre sur le champ1. L'optimiseur calculerait la sélectivité estimée pour chacun d'eux, puis les multiplierait pour déterminer leur sélectivité combinée. Mais dans de nombreux cas, cela sous-estime considérablement le nombre de lignes qui correspondent à la condition.

L'indication NO_INDEX élimine cette option de la réflexion de l'optimiseur, qui se contente donc de suivre le plan qu'il juge le meilleur, en utilisant éventuellement dans ce cas l'élimination des partitions sur la base de l'une des autres conditions de filtrage de la requête.

2voto

René Nyffenegger Points 14898

L'utilisation d'un index dégrade les performances des requêtes si elle entraîne plus IO disque par rapport à l'interrogation de la table avec un index.

Cela peut être démontré à l'aide d'un simple tableau :

create table tq84_ix_test (
  a number(15) primary key,
  b varchar2(20),
  c number(1)
);

Le bloc suivant remplit 1 million d'enregistrements dans cette table. Chaque 250ème enregistrement est rempli avec un rare value dans la colonne b tandis que toutes les autres sont remplies de frequent value :

declare
  rows_inserted number := 0;
begin

  while rows_inserted < 1000000  loop

        if mod(rows_inserted, 250) = 0 then

           insert into tq84_ix_test values (
               -1 * rows_inserted, 
               'rare value',
                1);

            rows_inserted := rows_inserted + 1;

        else

           begin
              insert into tq84_ix_test values (
                 trunc(dbms_random.value(1, 1e15)),
                'frequent value',
                 trunc(dbms_random.value(0,2))
               );
               rows_inserted := rows_inserted + 1;

           exception when dup_val_on_index then 
               null;
           end;

        end if;

  end   loop;

end;
/

Un index est placé sur la colonne

create index tq84_index on tq84_ix_test (b);

La même requête, mais une fois avec index et une fois sans index, diffère en performance. Vérifiez par vous-même :

set timing on

select /*+ no_index(tq84_ix_test) */
    sum(c)
  from 
    tq84_ix_test
  where
    b = 'frequent value';

select /*+ index(tq84_ix_test tq84_index) */
    sum(c)    
  from 
    tq84_ix_test
  where
    b = 'frequent value';

Pourquoi ? Dans le cas sans l'index, tous les blocs de la base de données sont lus, dans un ordre séquentiel. En général, cela est coûteux et donc considéré comme mauvais. En situation normale, avec un index, un tel "balayage complet de la table" peut être réduit à la lecture de 2 à 5 blocs de base de données d'index plus la lecture d'un bloc de base de données qui contient l'enregistrement sur lequel l'index pointe. Dans l'exemple qui nous occupe, c'est tout à fait différent : l'index entier est lu et pour (presque) chaque entrée de l'index, un bloc de base de données est lu également. Ainsi, non seulement la table entière est lue, mais aussi l'index. Notez que ce comportement serait différent si c étaient également dans l'index car dans ce cas, Oracle pourrait choisir de récupérer la valeur de c à partir de l'index au lieu de faire le détour par la table.

Donc, pour généraliser la question : si l'index ne sélectionne pas un grand nombre d'enregistrements, il peut être avantageux de ne pas l'utiliser.

2voto

walid Points 21

Il faut savoir que les index sont des valeurs précalculées basées sur l'ordre des lignes et les données du champ. Dans ce cas précis, vous dites que le champ 1 est indexé et vous l'utilisez dans la requête comme suit :

    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString'

Dans l'extrait de requête ci-dessus, le filtre porte à la fois sur un élément de données variable, puisque le caractère pourcentage (%) encadre la chaîne, et sur une autre chaîne spécifique. Cela signifie que l'optimisation Oracle par défaut, qui n'utilise pas d'indice d'optimisation, essaiera d'abord de trouver la chaîne à l'intérieur du champ indexé, puis de déterminer si les données sont une sous-chaîne des données du champ, et enfin de vérifier que les données ne correspondent pas à une autre chaîne spécifique. Après la vérification de l'index, les autres colonnes sont vérifiées. Ce processus est très lent s'il est répété.

L'indice NO_INDEX proposé par le DBA supprime la préférence de l'optimiseur pour l'utilisation d'un index et permettra probablement à l'optimiseur de choisir les comparaisons les plus rapides en premier et de ne pas nécessairement forcer la comparaison de l'index d'abord et des autres colonnes ensuite.

La méthode suivante est lente car elle compare la chaîne et ses sous-chaînes :

            field1_ like '%someGenericString%'

Alors que la suivante est plus rapide car elle est spécifique :

            field1_ like 'someSpecificString'

La raison d'utiliser l'indication NO_INDEX est donc que les comparaisons sur l'index ralentissent les choses. Si le champ de l'index est comparé à des données plus spécifiques, alors la comparaison de l'index est généralement plus rapide.

Je dis généralement car lorsque le champ indexé contient plus de données redondantes comme dans l'exemple mentionné par @Atish ci-dessus, il devra parcourir une longue liste de négatifs de comparaison avant de renvoyer une comparaison positive. Les astuces produisent des résultats variables car la conception de la base de données et les données contenues dans les tables affectent la rapidité d'exécution d'une requête. Ainsi, pour appliquer des conseils, vous devez savoir si les comparaisons individuelles que vous indiquez à l'optimiseur seront plus rapides sur votre ensemble de données. Il n'y a pas de raccourcis dans ce processus. L'application de conseils doit se faire après l'écriture des requêtes SQL appropriées, car les conseils doivent être basés sur les données réelles.

Consultez cette référence d'indices : http://docs.oracle.com/cd/B19306_01/server.102/b14211/hintsref.htm

0voto

Atish Points 51

Pour ajouter à ce que Rene' et Dave ont dit, c'est ce que j'ai réellement observé dans une situation de production :

Si la (les) condition(s) sur le champ indexé renvoie(nt) trop de correspondances, Oracle a intérêt à effectuer un Full Table Scan.

Nous avions un programme de rapport qui interrogeait une très grande table indexée - l'index était sur un code de région et la requête spécifiait le code de région exact, donc Oracle CBO utilise l'index.

Malheureusement, un code régional spécifique représentait 90 % des entrées des tableaux.

Tant que le rapport était exécuté pour l'un des autres codes de région (mineurs), il était terminé en moins de 30 minutes, mais pour le code de région majeur, il fallait plusieurs heures.

L'ajout d'un indice au SQL pour forcer un balayage complet de la table a résolu le problème.

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X