2009-06-26 6 views
2

Je travaille actuellement dans le déploiement d'un ERP basé OFBiz La base de données utilisée est Oracle 10g EnterpriseComment améliorer les performances dans Oracle en utilisant SELECT DISTINCT

L'un des plus grands problèmes est des problèmes de performance oracle, analyse les journaux ofbiz, la requête suivante:

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID, 
SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY, 
FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID, 
ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID, 
AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE, 
REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, 
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM 
ERP.ORDER_HEADER WHERE ((STATUS_ID = :v0 OR STATUS_ID = :v1 OR STATUS_ID = :v2) AND 
(ORDER_TYPE_ID = :v3)) ORDER BY ORDER_DATE DESC 

est très lent. Nous avons testé l'exécution de la requête sans DISTINCT et cela prend environ 30 secondes. Il y a 4.000.000+ registres dans la table. Il y a index pour le champ PK orderId et presque tous les autres domaines

EXPLAIN PLAN Distinct est:

SELECT STATEMENT() (null) 
SORT (ORDER BY) (null) 
    HASH (UNIQUE) (null) 
    TABLE ACCESS (FULL) ORDER_HEADER 

et sans DISTINCT est:

SELECT STATEMENT() (null) 
SORT (ORDER BY) (null) 
    TABLE ACCESS (FULL) ORDER_HEADER 

des idées sur Oracle tuning améliorer la performance de ce genre de requêtes? Il est très difficile de réécrire la requête car est générée automatiquement par OFBiz donc je pense que la solution est sur oracle d'accord

grâce à l'avance

EDIT: J'analysé la requête en utilisant tkprof, comme suggéré par Rob van Wijk et haffax, et le résultat est le suivant

******************************************************************************** 

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID, 
SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY, 
FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID, 
ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID, 
AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE, 
REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, 
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM 
ERP.ORDER_HEADER WHERE STATUS_ID = 'ORDER_COMPLETED' ORDER BY ORDER_DATE DESC 

call  count  cpu elapsed  disk  query current  rows 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
Parse  1  0.03  0.01   0   0   0   0 
Execute  1  0.00  0.00   0   0   0   0 
Fetch  1  9.10  160.81  66729  65203   37   50 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
total  3  9.14  160.83  66729  65203   37   50 

Misses in library cache during parse: 1 
Optimizer mode: ALL_ROWS 
Parsing user id: 58 

Elapsed times include waiting on following events: 
    Event waited on        Times Max. Wait Total Waited 
    ---------------------------------------- Waited ---------- ------------ 
    SQL*Net message to client      1  0.00   0.00 
    db file scattered read      8178  0.28  146.55 
    direct path write temp      2200  0.04   4.22 
    direct path read temp       36  0.14   2.01 
    SQL*Net more data to client      3  0.00   0.00 
    SQL*Net message from client      1  3.36   3.36 
******************************************************************************** 

il semble que le problème est le « fichier db dispersés lire », des idées sur la façon de régler Oracle afin de réduire l'attente à cet événement?

Suivez avec le nouveau résultat TKPROF, cette fois la fermeture de la session:

******************************************************************************** 

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID, 
SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY, 
FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID, 
ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID, 
AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE, 
REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, 
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM 
ERP.ORDER_HEADER WHERE STATUS_ID = 'ORDER_COMPLETED' ORDER BY ORDER_DATE DESC 

call  count  cpu elapsed  disk  query current  rows 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
Parse  1  0.03  0.01   0   0   0   0 
Execute  2  0.00  0.00   0   0   0   0 
Fetch  1  8.23  47.66  66576  65203   31   50 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
total  4  8.26  47.68  66576  65203   31   50 

Misses in library cache during parse: 1 
Optimizer mode: ALL_ROWS 
Parsing user id: 58 

Rows  Row Source Operation 
------- --------------------------------------------------- 
    50 SORT ORDER BY (cr=65203 pr=66576 pw=75025 time=47666679 us) 
3456659 TABLE ACCESS FULL ORDER_HEADER (cr=65203 pr=65188 pw=0 time=20757300 us) 


Elapsed times include waiting on following events: 
    Event waited on        Times Max. Wait Total Waited 
    ---------------------------------------- Waited ---------- ------------ 
    SQL*Net message to client      1  0.00   0.00 
    db file scattered read      8179  0.14   34.96 
    direct path write temp      2230  0.00   3.91 
    direct path read temp       52  0.14   0.84 
    SQL*Net more data to client      3  0.00   0.00 
    SQL*Net message from client      1  1510.62  1510.62 
******************************************************************************** 
+0

Les colonnes ORDER_DATE, order_type_id et status_id où annulable. Après les avoir réglées pour qu'elles ne soient pas NULL, la requête s'exécute en environ 50 secondes. Maintenant le plan avec distinct est égal au plan sans distinct. Le plus lent est le SORT – Ismael

Répondre

1

Si la différence entre les deux requêtes est importante, ce serait surprenant. Vous mentionnez que la requête sans DISTINCT prend environ 30 secondes. Combien de temps prend la requête avec DISTINCT? Pouvez-vous afficher la sortie tkprof de la requête avec DISTINCT, après avoir tracé la session avec un "alter set set events '10046 suivi du contexte pour toujours, niveau 8", et déconnecter après la fin de la requête? De cette façon, nous pouvons voir où le temps est réellement passé et s'il attendait quelque chose ("chemin direct read temp" peut-être?)

Cordialement, Rob.


Followup, après que le fichier a été publié tkprof:

Je vois que vous réussi à obtenir la sortie de tkprof, mais malheureusement, vous ne l'avez pas déconnecter votre session avant de créer le fichier tkprof. Maintenant, le curseur a été laissé ouvert et il n'a pas réussi à écrire des lignes STAT # dans votre fichier de trace. C'est pourquoi vous n'avez pas d'opération de source de plan/ligne dans votre fichier tkprof. Ce serait bien si vous pouvez répéter le processus, si la suggestion ci-dessous s'avère être des déchets.

Un peu de spéculation de mon côté: Je pense que le DISTINCT est presque un non-op parce que vous sélectionnez tant de colonnes. Si cela est vrai, alors votre prédicat "WHERE STATUS_ID = 'ORDER_COMPLETED'" est très sélectif et vous bénéficierez d'avoir un index sur cette colonne. Après avoir créé l'index, assurez-vous de l'analyser correctement, peut-être même avec un histogramme si les valeurs de données sont faussées. Le résultat final sera un plan différent pour cette requête, en commençant par un SCAN INDEX RANGE, suivi d'un TABLE ACCESS BY ROWID, conduisant à une requête très rapide.

Après avoir créé un index, vous pouvez avoir la table réanalysés, y compris à l'aide histogrammes cette déclaration:

exec dbms_stats.gather_table_stats ([le propriétaire], [nom_table], cascade => true, method_opt = > "POUR TOUTES LES TAILLES DE COLONNES INDEXÉES")

Cordialement, Rob.

2

Puisque vous commandiez résultats selon order_date, il est important que vous avez un indice descendant sur ce champ. Dites également à Oracle que vous souhaitez utiliser cet index. Placez le champ order_date d'abord dans la requête et utiliser une touche comme

SELECT /*+ index(HEADERS IDX_ORDER_DATE_DESC) */ ... 
FROM ERP.ORDER_HEADER HEADERS 
WHERE ... 
ORDER BY ORDER_DATE DESC 

Il n'est pas tant d'avoir des indices, mais plutôt de dire oracle de les utiliser. Oracle est très pointilleux sur les indices. Vous obtenez les meilleurs résultats lorsque vous choisissez des indices en fonction de vos requêtes les plus importantes. En cas de doute, trace a query. De cette façon, vous pouvez voir dans quelle partie de la requête l'oracle passe le plus de temps et si vos index sont effectivement récupérés ou non. Le suivi est inestimable pour lutter contre les problèmes de performances.

+2

Suggérer des indices dans des cas triviaux comme celui-ci, au lieu de laisser l'optimiseur basé sur les coûts faire son travail, n'est pas ma suggestion préférée. Et puis une remarque facile "Oracle est très pointilleux sur les indices" était en train de déplacer ma souris vers le bouton downvote. Je vais le laisser avec cette remarque seulement, car les trois dernières phrases sur le traçage étaient très sensées :-) –

1

ORDER_ID est-il déclaré comme PK à l'aide d'une contrainte PRIMARY KEY? Parce que si c'est le cas, je m'attendrais à ce que l'optimiseur reconnaisse que DISTINCT est superflue dans cette requête et l'optimise. Sans la contrainte, il ne saura pas que c'est superflu et déploiera ainsi des efforts inutiles et considérables pour «dé-duper» les résultats.

-1

Oracle accède à la table entière chaque fois que vous exécutez la requête (TABLE ACCESS (FULL)). Création d'un index sur les colonnes STATUS_ID et ORDER_TYPE_ID

CREATE INDEX ERP.ORDER_HEADER_I1 ON ERP.ORDER_HEADER (STATUS_ID, ORDER_TYPE_ID); 

aidera beaucoup, surtout s'il y a plusieurs valeurs différentes de STATUS_ID et ORDER_TYPE_ID dans la table ORDER_HEADER.

0

Lors du dépannage d'applications où je n'ai pas le contrôle du SQL, je trouve que le paquet dbms_sqltune fait gagner beaucoup de temps. Voir http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28419/d_sqltun.htm, et oui, malheureusement, vous devriez être autorisé à l'utiliser.

Il existe des procédures dans ce package pour exécuter une analyse d'optimisation par rapport à un identificateur sql_id spécifique dans le pool partagé ou dans le référentiel AWR. L'analyse contiendra des recommandations d'indexation s'il y a des améliorations à apporter avec des index supplémentaires. Plus important encore, l'analyseur pourrait découvrir un chemin d'accès amélioré qu'il peut implémenter avec ce qu'Oracle appelle un profil SQL - c'est un ensemble de conseils qui seront stockés et utilisés chaque fois que sql_id est exécuté. Cela se produit sans exiger que les conseils soient codés dans l'instruction SQL, et il existe également une option permettant de faire ce que vous pouvez imaginer comme une correspondance floue si votre application génère des valeurs littérales dans l'instruction au lieu des variables de liaison.

Bien sûr, cet outil ne doit pas se substituer à la compréhension de l'application et de ses structures de données, mais lire la sortie qui détaille le chemin d'exécution d'un meilleur plan peut être instructif.

Questions connexes