Paramètres

Transcription

Paramètres
ÉLÉMENTS AGISSANT SUR L'OPTIMISATION
Les principaux problèmes à prendre en compte dans les processus de tuning peuvent être scindés en trois
catégories :
La première étape du tuning consiste à identifier le problème. Éliminer les points qui ne posent pas problème
est aussi important que d'identifier les zones où se posent les problèmes.
L'OS devrait être la première zone vérifiée. Ensuite, le prochain niveau à vérifier est l'application. Enfin,
s'intéresser à l'instance.
On s'intéressera également, nécessairement à la partie optimisation SQL et aux bons choix fait vis-à-vis de
l'optimisation.
Au niveau OS
Mémoire, CPU, et I/O (disk et réseau). Vérifier par vmstat, top, etc.
Un bon repère est la mesure du taux de pagination (plus de demandes de mémoire que de mémoire
disponible. Avec comme solutions, redimensionnement de la RAM, déplacer des process sur d'autres
machines, limiter la taille mémoire qu'un process peut utiliser.
On peut aussi agir en écrivant des ordres SQL plus efficients. Une autre solution est le pool de connexions.
La demande de ressources CPU est mis en évidence par les files d'attentes. Tous les OS ont un scheduler
qui détermine quand un process vit et combien de temps il vit.
Si le problème réside dans les I/O (longues files d'attente ou des taux élevés de requêtes), on peut
envisager des solutions telles data stripping ou ajout de disques. On peut aussi vérifier l'adéquation entre les
caractéristiques de l'OS et les paramètres globaux choisis pour l'instance (ex :
db_file_multi_block_read_count).
Au niveau de l'application
Serialisation des ressources
La sérialisation des ressources peut provenir de nombreux cas de figure. Par exemple, une activité qui
nécessite que tous les chèques doivent avoir des identifiants consécutifs et de progression continue (pas de
"trous"). Cette contrainte ne peut être gérée que par une source unique de valeurs, ie les séquences.
ex : 1 sequence -uniques + consécutif, pour plusieurs process
Schéma relationnel
! à la sur-normalisation qui provoque trop de jointures et à la sous normalisation qui provoque des
contraintes supplémentaires
Gestion des sessions
ex : une application web qui nécessite connexion et déconnexions continuelles
Curseurs et design du programme
Les deux requêtes :
SELECT * FROM hr.employees WHERE employee_id = 7900; et
SELECT * FROM hr.employees WHERE employee_id = 7369;
n'utiliseront qu'un seul curseur s'il existe un index sur employee_id même si le texte SQL est différent et que
le paramètre CURSOR_SHARING = FORCE
1/15
Statistiques pour la SGA
Diagnostic Tools for Tuning the Shared Pool
The V$SGASTAT or V$SGAINFO view displays the sizes of all SGA structures. V$SGASTAT is more detailed
for the various pieces of the shared pool. V$SGAINFO provides a summary including the granule size and
whether the memory area is resizable.
The contents of the shared pool are not aged out as long as free memory is available in the shared pool. Use
the following dynamic views to diagnose performance issues related to the library cache:
V$LIBRARYCACHE: Statistics on library cache management
V$LIBRARY_CACHE_MEMORY: Statistics on memory use for each of the namespaces
V$SQLAREA: Full statistics about all shared cursors and the first 1,000 characters of the SQL statement
V$SQL: Statistics about shared SQL area; contains one row for each child of the original SQL text entered.
The V$SQL view is a similar view to V$SQLAREA, except that it does not include a GROUP BY clause that can
make the V$SQLAREA view expensive to query. A SELECT of V$SQL is a latched operation.
V$SQLSTATS: A low impact view for use instead of the other V$SQL* views. V$SQLSTATS uses a mutex
rather than a latch.
V$SQLTEXT: The full SQL text without truncation, in multiple rows
V$DB_OBJECT_CACHE: Database objects cached, including packages; also objects such as tables and
synonyms, where these are referenced in SQL statements
The Statspack and AWR reports contain information from these views and also contain information about
SQL statements found in the library cache when either of the snapshots was taken.
Les performance sont impactées lorsque l'on a des « contention » pour une ou plusieurs ressources. Il y a
typiquement les ressources hardware (par exemple CPU, memoire, réseau, et disque) et software ou les
ressources applicatives (row lock, verrous, etc).
2/15
LES INDEX
Optimisations réalisées automatiquement
•
•
Utilisation des ordres DISTINCT : dans ce cas, Oracle trie les lignes, écarte les valeurs dupliquées dès qu'elles sont
rencontrées. Les index ne sont pas utilisés pour traiter cette clause
Utilisation des clauses GROUP BY : même remarques que pour les clauses DISTINCT
Critères de choix pour les index







Si la table doit subir beaucoup d'INSERT créer l'index après le remplissage sauf pour des cas où l'index sert à
vérifier l'unicité des valeurs
S'il est utilisé par un cluster, le créer avant toute insertion
Les colonnes de type LONG et LONG RAW ne peuvent être indexées
Limiter le nombre d'index (3 est une bonne moyenne maximale)
Affecter éventuellement des tablespace ad hoc.
Eviter de choisir des colonnes à indexer avec peu de dispersion (contenant des valeurs à peu de dispersion
comme oui/non, …)
Consulter le Administrator's Guide pour les calculs de la taille et des paramètres de stockage de l'index.
Ecriture d'ordres SQL tirant parti des index
Un index peut être utilisé si :
 il est référencé dans un prédicat
 la colonne indexée n'est pas modifiée par une fonction ou une opération arithmétique.
Un index sera utilisé si :
 l'optimiseur de requêtes décide que son usage est approprié.
Un index ne sera pas utilisé si :
 il n'y a pas de clause WHERE
 le prédicat modifie la colonne d'une façon quelconque
 la recherche se fait explicitement pour des lignes comportant des valeurs NULL ou NOT NULL dans la colonne
indexée (le prédicat contient les opérateurs IS / IS NOT NULL).
Si la colonne doit être modifiée, l'index ne sera pas utilisé, comme par exemple, dans :
where SAL * 12 = 24000
where SAL + 0 = 500
where ' ' | | ENAME = ' Smith'
Une exception, cependant, existe à cette règle : une optimisation spéciale en vue de trouver la plus petite
ou plus grande valeur dans la tableautorise le SELECT à comporter une expression contenant
{ MIN | MAX } (col { + | - } constante )
sans aucune autre colonne, comme dans : Select 2 * MAX (SAL+1) from emp;
Création d'index efficaces.
Les index concaténés peuvent être d'un grand intérêt pour l'amélioration de l'efficacité des requêtes.
Supposons qu'un index ait été créé au moyen de la commande :
Create index Ind_Nom_Ville_Etat
on Employee (Last_Name, City, State)
Lorsque la clause Where utilise les mêmes colonnes que celles de l'index, celui-ci sera utilisé dans la requête :
Select ... from employee
where Last_Name = 'Smith'
and city = 'Paris'
and state = 'MD'
Si seulement la première colonne de l'index est citée dans la clause where, l'index sera encore utilisé, mais il risque
d'être moins sélectif que dans l'exemple précédent :
Select ...
where Last_Name = 'Jones'
Si dans la clause where n'est pas citée la première colonne, l'index ne sera pas utilisé. ainsi :
Select ...
where city = 'Paris'
3/15
and state = 'MD'
Une requête donnée peut être formulée de façons différentes pour chercher ou éviter d'utiliser un index. Ainsi, en
supposant que la colonne Hiredate est de type date, et possède un index non-unique, la requête :
Select * from emp
where to_char(Hiredate, 'Month dd, yyyy') = 'January 14, 1992')
modifie la colonne Hiredate, et l'index ne sera pas utilisé, alors que dans la requête :
Select * from emp
where Hiredate = to_date('January 14, 1992','month dd, yyyy')
la constante (January 14, 1992) sera convertie en date, et Oracle utilisera l'index.
La requête suivante utilisera également l'index :
Select * from emp
where Hiredate = '14-JAN-92'
Sélectivité d'un index
Généralités
La sélectivité est l'un des paramètres fondamentaux à prendre en compte lors de la création des index quant
à leur efficacité et leur bien fondé. La séléctivité se calcule comme ceci:
Ratio entre le nombre de valeurs distinctes sur le nombre de lignes de la table. Si une table a 1000 lignes et
une colonne indexée de cette table, 950 valeurs distinctes, alors la sélectivité de cet index sera de 950/1000,
soit 0.95. La meilleure sélectivité d'un index sera de 1.0 (cas des index uniques). Ce critère est important.
Prenons le cas d'un index de 1000 lignes avec une sélectivité de 0.005. Cela signifiera que pour chaque
valeur distincte de l'index, 200 lignes seront ramenées en moyenne. Dans ce cas, il est probable qu'un
balayage complet de la table sera plus efficace.
Un index ayant une sélectivité se rapprochant de 1, sera le plus efficace. On considère qu'un index
commence à être bon, lorsque sa sélectivité arrive à 0.75.
Mesure manuelle de la sélectivité
Soit une colonne ename, à indexer. La requête suivante permet de connaître le nombre de valeurs distinctes
sur la colonne :
Select count (distinct ename)
from emp;
Puis :
Select count(*)
from emp;
permet de connaître le nombre total de lignes de la table.
L'intérêt de la méthode manuelle est qu'elle permet d'évaluer l'intérêt d'un index avant sa création.
Utilisation de la commande analyze.
Lors de l'analyse d'une table, tous les index qui lui sont attachés, sont analysés également. Ainsi :
Analyze table EMP compute statistics;
La requête suivante donne le nombre de clés distinctes pour notre index :
select DISTINCT_KEYS
from USER_INDEXES
where TABLE_NAME = 'EMP'
and INDEX_NAME = 'EMP$ENAME'
Le nombre total de lignes de la table est dans USER_TABLES.
select NUM_ROWS
from USER_TABLES
where TABLE_NAME = 'EMP';
4/15
TABLESPACES TEMPORAIRES
Pour nombre d'opérations de tri (order, distinct, group by, …), Oracle utilise des segments temporaires. Il est
souvent intéressant de réserver un tablespace spécifique à ce type d'opérations pour soulager le système. A
cette fin, le meilleur choix consiste à créer et réserver un tablespace à de telles opérations "temporaires".
Pour désigner un tablespace à ce type de fonctionnement, on peut y parvenir par :
create/alter tablespace TEMP temporary;
et en finir par :
create/alter tablespace TEMP permanent;
Principales précautions :
initial et next identiques, pctincrease =0
Pour les grosses opérations de tri, choisir de grandes tailles de segments
Alter tablespace TEMP coalesce (pctincrease=0)
utiliser la vue DBA_SEGMENT
CLUSTERS ET PROXIMITÉ PHYSIQUE DES DONNÉES
La proximité des lignes en fonction d'un ordonnancement très utilisé sur telle ou telle colonne peut avoir un
impact déterminant sur les performances des requêtes sur de grandes tables.
On peut songer dans ce cas à les organiser en clusters. Malheureusement, les clusters offrent des
performances très mauvaises en transactionnel : 6 à 8 fois plus lentes que des tables non clusterisées et
génèrent des quantités d'informations bien plus importantes sur les aspects rollback et redo logs.
Dans l'idéal, on peut penser à réordonner les lignes d'une table en créant une seconde table, via une
commande CREATE AS SELECT, ou INSERT AS SELECT. Par malchance, ces deux commandes ne
supportent pas la clause order by. Pour contourner cette difficulté, en supposant que sur notre table des
employés EMP, la plupart des requêtes se fasse sur la colonne ENAME, créer une vue :
create or replace view EMP_VIEW as
selectENAME,
JOB,
EMPNO,
MGR,
SAL,
COMM,
HIREDATE,
DEPTNO
from EMP
group by ENAME,
JOB,
EMPNO,
MGR,
SAL,
COMM,
HIREDATE,
DEPTNO,
ROWNUM;
La clause group by forcera l'opération de tri et les lignes selectionnées par l'appel à la vue seront triées
comme prévu sur le champ ENAME. L'ajout du champ rownum permet d'éviter l'élimination des doublons par
leur regroupement.
Ensuite il ne reste plus qu'à créer effectivement une nouvelle table à partir de la vue :
create table EMP_TRIE
as select * from EMP_view;
5/15
L'OPTIMISEUR : RBO, CBO ET HINTS
L'optimiseur peut fonctionner sur deux modes :
- Heuristique (Rule Based Optimizer – RBO)
- Statistique (Cost Based Optimizer – CBO)
Le choix se fait à partir du fichier init.ora en fonction du paramètre
OPTIMIZER_MODE = {RULE | CHOOSE | FIRST_ROWS | ALL_ROWS}
(Voir la doc "Reference" d'Oracle, section Initialization parameters).
Le résultat de l'optimisation est un plan d'exécution, ie une séquence d'opérations à exécuter. A la suite de
quoi, il reste à appliquer les techniques d'accès appropriées pour chaque opération du plan d'exécution, et
gérer les flots de données entre chaque opération.
Le résultat du plan d'exécution est un ensemble d'opérations de niveau intermédiaire, dit algèbre physique
constituée :
- De chemins d'accès aux données
- D'opérations manipulant les données (correspondant aux noeuds internes de l'arbre de requête).
Beaucoup d'infos supplémentaires dans la doc "Tuning" d'Oracle.
Etapes à suivre
1/ il faut traduire une requête exprimée en langage déclaratif en une suite d'opérations (similarité avec les
opérateurs de l'algèbre relationnelle)
2/ En fonction des coûts, il faudra estimer la meilleure stratégie.
3/ On obtient le plan d'exécution. Il n'y a plus qu'à le traiter au niveau physique.
Le mode Rule based optimizer
Tableau des chemins d'accès disponibles en mode RBO
Path 1: Single Row by Rowid
Path 2: Single Row by Cluster Join
Path 3: Single Row by Hash Cluster Key with Unique or Primary Key
Path 4: Single Row by Unique or Primary Key
Path 5: Clustered Join
Path 6: Hash Cluster Key
Path 7: Indexed Cluster Key
Path 8: Composite Index
Path 9: Single-Column Indexes
Path 10: Bounded Range Search on Indexed Columns
Path 11: Unbounded Range Search on Indexed Columns
Path 12: Sort-Merge Join
Path 13: MAX or MIN of Indexed Column
Path 14: ORDER BY on Indexed Column
Path 15: Full Table Scan
6/15
Le mode Cost based optimizer
Basé sur les statistiques et en particulier sur la commande analyze :
exec DBMS_UTILITY.ANALYZE_SCHEMA('APP_OWNER','COMPUTE');
si on veut passer du mode CBO en mode RBO :
ANALYZE table SALES delete statistics;
Lors de la re-génération de statistiques il n'est pas nécessaire de supprimer auparavant les précédentes
statistiques.
On peut définir le mode de fonctionnement par défaut de l'optimiseur en fixant la valeur du paramètre
optimizer_mode du fichier init*.ora
Si Oracle dispose de statistiques, il utilisera par défaut le mode CBO. Si ces statistiques sont supprimées
(par la commande analyze ... delete statistics) il se rabattra automatiquement sur le mode RBO.
Plusieurs tables sont impliquées, dont DBA_TABLES, plusieurs tables V$.
Pourquoi utiliser des hints
Il peut être nécessaire d'utiliser les hints parce que l'optimiseur est un programme, prévu pour tenir compte
d'un certain nombre fini, de paramètres. Il est clair que dans le monde des bases de données, la quantité de
paramètres pouvant influer de façon significative sur les performances est extrêmement variable en nombre,
en qualité et en valeur, sans parler de la dépendance envers le contexte.
Il est donc probable que aussi raffinés qu'ils pourront être, les algorithmes mis en oeuvre par cet optimiseur
présenteront ponctuellement mais à coup sûr des points faibles, des imperfections.
Les hints sont des ordres que l'on peut glisser dans une requête SQL, qui seront interprétés prioritairement
par l'optimiseur, afin d'orienter ou redéfinir un plan d'exécution décidé par le rédacteur (averti) de la requête.
Ci-dessous, on peut lire un exemple de requête utilisant un hint :
select /*+ USE_HASH(COMPANY) CACHE(COMPANY) */
company.name, sum(sales.dollar_amount)
from company, sales
-- company est une petite table et sales est grande
where company.company_id=sales.company_id
group by company.name;
7/15
EXPLAIN PLAN - OPÉRATIONS DE LA BASE
Génération de plan
L'ordre général des opérations est le suivant :
- Créer la table qui réceptionnera le plan. Oracle fournit un script qui réalise cette création :
$ORACLE_HOME/rdbms/admin/utlxplan.sql
- Génération du plan par la commande explain plan
- Requête sur la table plan_table àplacer éventuellement dans un script SQL :
REM montre le plan et l'efface de la table plan_table
REM
set TRUNCATE on
set PAGESIZE 128
COL OPERATION_TREE FORMAT A30
COL OPTIONS FORMAT A24
COL OBJECT_NAME FORMAT A24
SELECT LPAD(' ',2*LEVEL)||OPERATION OPERATION_TREE,OPTIONS,OBJECT_NAME
FROM PLAN_TABLE
CONNECT BY PRIOR ID = PARENT_ID
START WITH ID = 1
ORDER BY ID ;
delete from PLAN_TABLE;
Opérations utilisées dans la base
Les opérations utilisées dans la base sont de deux types : opérations d'ensembles ou bien de lignes. Les
différences essentielles sont :
Row operations
Set operations
Exécutées sur une ligne à la fois
Exécutées sur un ensemble de lignes résultant
Exécutées au niveau du FETCH
Exécutée au niveau de l'EXECUTE, ie à l'ouverture
du curseur
Les premiers résultats arrivent avant la fin de
l'exécution
Les résultats arrivent après la fin de l'exécution
Ex : un full table scan
Ex : un full table scan avec une clause de group by
Ci-après, quelques exemples d'opérations les plus courantes sont cités, accompagnées de quelques
commentaires. Pour une lecture exhaustive, se référer à la documentation Oracle (Tuning en particulier).
Opération
Set ou row
Usage
AND-EQUAL
r
Quand deux colonnes ou plus avec un index
monocolonne non-unique sont impliquées
BITMAP
r
Quand des index bitmap sont utilisés
HASH JOIN
s
Dans une opération de jointure quand un ou plusieurs
résultats intermédiaires risquent d'être très grands.
INDEX FULL SCAN
r
Pour des opérations sur des index monocolonnes
INDEX RANGE SCAN
r
Quand sont impliqués des opérateurs tels between,
like, etc
INDEX UNIQUE SCAN
r
Pour un test d'égalité sur toutes les colonnes d'un
index unique
MERGE JOIN
r
Pour les opérations de jointure sur des tables
dépourvues d'index sur les colonnes utilisées dans la
condition de jointure
NESTED LOOPS
r
Très communément utilisé dans des jointures
impliquant dans les conditions de jointure au moins
une colonne indexée.
REMOTE
r/s
Présence de database links
8/15
Opération
Set ou row
Usage
SORT JOIN
s
Sous produit de l'opération MERGE JOIN
TABLE ACCESS BY ROWID
r
Accès à une table via un index. Nommée aussi "user
rowid"
TABLE ACCESS FULL
r
Pour accéder à une table lorsqu'on ne peut pas
appliquer de conditions sur des colonnes indexées
Exemples de plans
Pour les opérations les plus fréquentes, quelques exemples de requêtes, leur plan et leur interprétation.
HASH JOIN
select compagnie.nom
from compagnie,ventes
where compagnie.compagnie_id=ventes.compagnie_id
and ventes.period_id = 3
and ventes.ventes_total>1000;
Le plan :
HASH JOIN
TABLE ACCESS FULL VENTES
TABLE ACCESS FULL COMPAGNIE
Si l'une des tables est notablement plus petite que l'autre et qu'elle tient en mémoire, l'optimiseur utilisera
cette opération plutôt qu'une jointure NESTED LOOP. Même si un index est disponible une telle opération
peut s'avérer préférable à une jointure NESTED LOOP.
MERGE JOIN
select compagnie.name
from compagnie, ventes
where compagnie.compagnie_id+0=ventes.compagnie_id+0
and ventes.period_id = 3
and ventes.ventes_total>1000;
Le plan :
MERGE JOIN
SORT JOIN
TABLE ACCESS FULL VENTES
SORT JOIN
TABLE ACCESS FULL COMPAGNIE
Les index sont neutralisés et ne peuvent donc être utilisés.
Oracle fait un full scan (TABLE ACCESS FULL) sur chaque table, trie le résultat (SORT JOIN) et fusionne le
résultat.
TABLE ACCESS BY ROWID
select nom
from compagnie
where compagnie_id = 12345
and active_flag = 'O';
Le plan :
TABLE ACCESS BY ROWID COMPAGNIE
INDEX UNIQUE SCAN COMPAGNIE_PK
L'utilisation de la colonne compagnie_id dans la clause where de la requête autorise l'usage de l'index
COMPAGNIE_PK. Si cet index ne contient pas également la colonne nom, oracle devra pour y accéder
utiliser la valeur du rowid retournée par l'index. Si l'index a été créé en concaténant compagnie_id et nom la
valeur du nom sera retournée directement.
9/15
Interpréter un plan
L'interprétation se fait de l'intérieur vers l'extérieur et du haut vers le bas. Les premières opérations
s'exécutant seront situées vers les "feuilles" de l'arbre dont la racine sera en haut à gauche.
Exemple de plan :
10 PROJECTION
9
SORT UNIQUE
8
UNION-ALL
5
MERGE JOIN
2
SORT JOIN
1
TABLE ACCESS FULL COMPANY
4
SORT JOIN
3
TABLE ACCESS FULL SALES
7
TABLE ACCESS BY ROWID COMPETITOR
6
INDEX UNIQUE SCAN COMPETITOR_PK
Les chiffres à gauches sont donnés à titre indicatif de l'ordre dans lequel se déroulent les opérations.
10/15
CONSEILS POUR L'OPTIMISATION
Eviter les full scan non désirés
Il y a deux raisons majeures d'éviter les full scans (balayages complets -séquentiels, des tables) :
- Ils ne sont pas sélectifs
- Les données lues via des full scans, sont rapidement supprimées de la SGA
Quand les full scans sont-ils utilisés :
- pas d'index sur la table
- pas de conditions (ie pas de clause where)
- il y a des conditions utilisées dans la requête, mais correspondant aux cas prévus de non utilisation des
index par l'optimiseur – voir paragraphe sur les index, ci-dessus.
Utiliser seulement des index sélectifs
Voir ci-dessus aspects généraux sur la gestion des index
Gérer les jointures multitables
Oracle fournit trois opérations de jointure : nested loops ie boucles imbriquées, merge join ie tri fusion, et
hash join – jointures hash.
Merge join -tri fusion
Cette opération requiert les étapes suivantes :
- Accès full table de chaque table de la jointure
- Tri des résultats (sort join)
- fusion des résultats triés (merge join des résultats du sort join)
Cet algorithme est utilisé lorsque les index sont absents ou inexploitables. Il travaille sur des ensembles de
données (lignes). Il ne retourne pas de lignes tant que tout le processus n'est pas terminé.
Les résultats ne seront pas disponibles longtemps dans la SGA.
Il peut être nécessaire d'allouer des segments temporaires
Conséquences sur l'optimisation:
Les merge joins seront efficaces :
- là où les full scans seront efficaces, ie lorsqu'ils seront préférables à l'utilisation d'index,
- petites tables
- voir "Gérer les très grandes tables" ci-après.
- Durant l'opération de tri, oracle utilise autant que faire se peut une zone de la mémoire appellée sort area.
La taille de cette zone est contrôlée par le paramètre sort_area_size. On peut améliorer les performances de
cette étape en utilisant aussi les sort_direct_writes (voir ci-dessus tableau paramètres).
- Les deux premières étapes sont les plus coûteuses en terme de performances.
- Mettre à disposition un tablespace spécifique de type temporaire (create ou alter) :
alter tablespace TEMP_1 temporary;
Boucles imbriquées - Nested loops
C'est le moyen le plus couramment utilisé pour les jointures. cette opération retourne très rapidement les
premiers résultats. Utilisée s'il y a un index sur une table.
La première opération effectuée par l'optimiseur est le choix de la table directrice. Cela peut être un full scan.
Pour chaque ligne de la table directrice, un accès par index est réalisé sur la table secondaire. Si une
correspondance existe, la ligne est retournée par l'opération nested loop.
Par exemple, considérons la requête suivante :
Select societe.nom
from societe, ventes
where societe.ste_id=ventes.ste_id
and period=2;
11/15
On suppose que societe.ste_id est une cle primaire de la table societe et la colonne ste_id pour la table
ventes. L'optimiseur pourra utiliser l'index en se basant sur la condition de jointure. Le plan d'exécution est :
NESTED LOOPS
TABLE ACCESS FULL VENTES
TABLE ACCESS BY ROWID SOCIETE
INDEX UNIQUE SCAN SOCIETE_PK
La première étape de l'exécution sera le full scan sur ventes. Pour chaque enregistrement de la table ventes,
l'index de SOCIETE_PK sera utilisé pour rechercher une correspondance.
Les étapes nécessaires à l'exécution de cette commande :
- Full table scan
- Parcours indexé à la recherche de valeurs uniques sur la table secondaire
- Si une correspondance est trouvée, accès par rowid à la ligne dans la table secondaire.
Conséquences sur l'optimisation:
La clé des performances des opérations en nested loops tient principalement à l'ordre dans lequel les tables
sont jointes. La sélection de la table directrice est tout à fait critique. Si la jointure implique plus de deux
tables, ce choix est encore plus critique. Le temps mis pour exécuter une jointure dans ces conditions, peut
croître de façon exponentielle. Dans l'exemple précédent, la table ventes était la table directrice. Les points
importants qu'il faut noter sur cet aspect :
- Bien que les colonnes de la table ventes citées dans la condition de jointure soient indexées (clé primaire),
l'index n'a pas été utilisé. Ceci parce la colonne indexée de la table ventes citée, n'a été utilisée que comme
condition de jointure et non comme restriction dans la clause where.
- L'optimiseur aurait tout aussi bien pu choisir la table societe comme table directrice et elle aurait subi le full
scan plutôt que la table ventes.
- En mode RBO, la table directrice eut été la dernière citée dans la clause from.
- En mode CBO l'optimiseur tiendra compte de la taille des tables, et de la sétectivité des index avant de
choisir une table directrice.
Comment influer sur le "join path" :
- En mode CBO on peut utiliser les hints. Les hints susceptibles d'influer sur le mode d'exécution d'une
jointure sont :
ORDERED jointure les tables en s'appuyant sur leur ordre dans la clause from
INDEX
lister un index spécifique à utiliser
FULL
indiquer une table à lire en full scan, ce qui peut en faire implicitement la table directrice
USE_NL
liste les tables à joindre en nested loop.
- on peut anihiler l'utilisation d'un index (voir ci-dessus les modalités de mise en oeuvre des index)
Jointures hash – hash join
L'optimiseur peut décider dynamiquement de traiter une requête par une opération HASH JOIN en lieu et
place d'un NESTED LOOP ou d'un MERGE JOIN. Si tel est le cas sur la même requête que dans l'exemple
précédent :
Select societe.nom
from societe, ventes
where societe.ste_id=ventes.ste_id
and period=2;
le plan d'exécution devient :
HASH JOIN
TABLE ACCESS FULL VENTES
TABLE ACCESS FULL SOCIETE
NB: Il ne faut pas confondre l'opération hash join avec les hash clusters qui sont une façon de stocker les
données. Les hash join renvoient à la façon dont Oracle exécute une jointure.
Durant une HASH JOIN deux tables sont jointes sur la base d'un ensemble de conditions de jointure telle
celle de notre requête :
where societe.ste_id=ventes.ste_id
Durant la jointure, une des tables impliquées (ici ventes) est lue via un full table scan et une image (bitmap)
des valeurs uniques des colonnes jointes de la table est construite en mémoire. Durant leur lecture, les
lignes sont séparées en groupes ou partitions en se basant sur une fonction de hash qui permet de mapper
(faire correspondre) les valeurs de jointure à la partition. Le hashage au niveau partition permet de séparer
les deux tables en ensembles correspondants aux critères utilisés par la jointure. Ce type de hashing permet
de traiter des jointures de grandes tables en limitant des pénalités trops lourdes en E/S. En plus de
partitionner les tables, l'optimiseur construit une table de hashage en mémoire pour toutes les partitions de
la table qui tiennent en mémoire. C'est une seconde fonction de hash qui est appliquée sur les valeurs de
12/15
jointure. l'image des valeurs créées sont ensuite utilisées comme filtre pour traiter les lignes de la deuxième
table (SOCIETE).
Si une ligne est repérée comme étant présente dans le bitmap, l'algorithme de hash est utilisé pour
rechercher les données.
Si les deux tables sont importantes, cet algorithme est également utilisé. Oracle met toujours en oeuvre deux
niveaux de hashage, ce qui a pour conséquence d'importantes économies d'E/S.
Plusieurs paramètres d'initialisation d'Oracle concernent la gestion des hash join :
HASH_JOIN_ENABLED (v8)
Doit être positionné à TRUE pour que l'optimiseur utilise ce
mécanisme
HASH_AREA_SIZE
Fixe la taille de la zone de tri utilisée pour cette opération. Par
défaut, sa valeur est double de celle du paramètre
SORT_AREA_SIZE
HASH_MULTIBLOCK_IO_COUNT
Fixe le nombre de blocs à lire en une fois pour les opérations d'E/S
de cette opération. Par défaut, égale à
DB_FILE_MULTIBLOCK_READ_COUNT
Sur le plan des ressources, il faut noter qu'il est indispensable de disposer de ressources matérielle (CPU et
RAM) de très bon niveau pour mettre en oeuvre cette fonctionnalité.
Ces types de jointures sont avantageuses lorsque les conditions suivantes sont réunies :
- La jointure est exécutée dans le cadre d'application batch (peu ou pas d'accès concurrents)
- La jointure est réalisée entre deux grandes tables (significativement plus importantes que le buffer cache).
- La jointure se fait entre une grande et une petite table. Dans ce cas, il est intéressant de forcer la mise en
cache de la petite table, en utilisant un hint :
select /*+ USE_HASH(COMPANY) CACHE(COMPANY) */
company.name, sum(sales.dollar_amount)
from company, sales
-- company est une petite table et sales est grande
where company.company_id=sales.company_id
group by company.name;
Dans cette requête, le hash join sera plus efficace que l'opération NESTED LOOP, car l'accès à la table
company ne sera fait qu'une fois là où il aurait été fait X fois dans le cas du NESTED LOOP. La table sales
n'est pas en cache car elle est de toute façon trop grosse pour tenir dans le buffer cache.
Eviter les appels de fonction PL/SQL
Gérer l'usage des bind variables
Les éviter si possible en utilisant des valeurs en dur ou des variables.
Revoir les process d'optimisation
Par des mesures de statistiques.
Se focaliser sur les requêtes SQL "offensives" (ie gourmandes).
Histogrammes
Ils sont utilisés pour améliorer la vision réelle de la distribution des valeurs d'une colonne. Ils permettent une
meilleure estimation de la sélectivité d'un index.
Soit par exemple une table qui a une ligne pour chaque personne sur terre. La table peut avoir deux
colonnes, une pour le nom du pays et l'autre pour le nom des personnes, telle que :
Create table population
(pays varchar2(200),
nom varchar2(200));
On trouvera 250 pays dans le monde et 6 milliards d'habitants ce qui fait une moyenne de 24 millions
d'habitants par pays. Pour accélérer les requêtes on crée l'index suivant :
create index population$pays
on population(pays);
13/15
La sélectivité de cet index sera de 0,0000000416 ce qui devrait le disqualifier dans tous les cas. Cependant
cet index pourrait être utile pour les requêtes concernant les pays à faible populations. Pour les pays à forte
population il sera probablement plus efficace de faire un full scan plutôt que d'utiliser l'index sur une requête
du type intervalle indexé. Pour les autres valeurs l'index pourrait bien être utile.
Oracle utilise des histogrammes pour déterminer la distribution des données. Plus précisément, des
histogrammes à colonnes de hauteur fixe (height-balanced histogram) . Le nombre de colonnes est choisi, et
le même nombre de valeurs sont placées dans chaque colonne, ce qui donnera pour chaque colonne une
"largeur" différente, un écart entre valeurs basse et haute, qui variera d'une colonne à l'autre.
Est-ce que l'index sera utilisé? Si les histogrammes ont été générés, l'optimiseur disposera de cette analyse
fine de la distribution des données.
On peut avoir une idée de l'histogramme généré, comme dans l'exemple ci-dessous où on voit un exemple
de requête permettant d'obtenir le contenu d'un histogramme.
SELECT endpoint_number, endpoint_value
FROM USER_HISTOGRAMS
WHERE table_name = 'INVENTORIES' and column_name = 'QUANTITY_ON_HAND'
ORDER BY endpoint_number;
ENDPOINT_NUMBER
--------------0
1
2
3
4
5
6
7
8
9
10
ENDPOINT_VALUE
-------------0
27
42
57
74
98
123
149
175
202
353
Sur ce listing, une ligne correspond à une colonne de l'histogramme.
Dans un tel histogramme, les valeurs de la colonne sont divisées en bandes telles qu'elles contiennent
approximativement le même nombre de lignes. L'information utile que fournit l'histogramme est
d'indiquer où se trouve le point terminant l'intervalle de valeurs.
Autre représentation :
Height-Balanced Histogram avec une distribution uniforme
Height-Balanced Histogram avec une distribution non uniforme
Les histogrammes s'avèreront donc utiles dès que l'on a affaire à des colonnes indexées à distribution
irrégulière.
La mise en oeuvre des histogrammes se fera par la commande
analyze table ventes compute statistics for table for all indexed columns size 250;
Dans ce cas on génèrera 250 colonnes pour notre histogramme.
Les paramètres importants à prendre en compte :
- nombre de colonnes (buckets)
- fréquence des analyze
- paramètre compute ou estimate de la commande analyze.
Informations complémentaires : documentation Oracle Tuning
14/15
Récapitulatif de comparaison de l'accès aux données
Sequentiel
Simple
Très coûteux
Indexé
Efficace – bonne gestion
des intervalles
Peu évolutif
Arbre-B
Hachage
Efficace – bonne gestion Le plus efficace
des intervalles
Pas de gestion des
Pb : traversée
intervalles
Tables statiques
15/15

Documents pareils