Domanda

Attualmente sto lavorando alla distribuzione di un ERP basato su OFBiz Il database utilizzato è Oracle 10g Enterprise

Uno dei maggiori problemi sono alcuni problemi relativi alle prestazioni dell'oracolo, che analizzano i log ofbiz, la seguente query:

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

è molto lento. Abbiamo testato l'esecuzione della query senza DISTINCT e sono necessari circa 30 secondi. Ci sono oltre 4.000.000 di registri nella tabella. Esistono indici per l'ordine dei campi PK e quasi tutti gli altri campi

Il piano EXPLAIN con DISTINCT è:

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

e senza DISTINCT è:

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

qualche idea sull'oracolo di ottimizzazione per migliorare le prestazioni di questo tipo di query? È molto difficile riscrivere la query perché viene generata automaticamente da ofbiz quindi penso che la soluzione riguardi la messa a punto dell'oracolo

grazie in anticipo

EDIT: ho analizzato la query usando tkprof, come suggerito da Rob van Wijk e haffax, e il risultato è il seguente

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

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
********************************************************************************

Quindi sembra che il problema sia la 'lettura di file sparsi in db', qualche idea su come ottimizzare l'oracolo per ridurre l'attesa in questo evento?

Segui il nuovo risultato tkprof, questa volta chiudendo la sessione:

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

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
********************************************************************************
È stato utile?

Soluzione

Se la differenza tra le due query è sostanziale, sarebbe sorprendente. Dici che la query senza DISTINCT richiede circa 30 secondi. Quanto tempo impiega la query con DISTINCT?

È possibile mostrare l'output di tkprof della query con DISTINCT, dopo aver tracciato la sessione con un " modificare il contesto del nome di traccia degli eventi di set di sessioni '10046 per sempre, livello 8' " e disconnettersi dopo che la query è terminata? In questo modo possiamo vedere dove viene effettivamente trascorso il tempo e se stava aspettando qualcosa (& Quot; percorso diretto leggi temp & Quot; forse?)

Saluti, Rob.


Seguito, dopo la pubblicazione del file tkprof:

Vedo che sei riuscito a ottenere l'output di tkprof, ma sfortunatamente non hai disconnesso la sessione prima di creare il file tkprof. Ora, il cursore è stato lasciato aperto e non è stato possibile scrivere le righe STAT # nel file di traccia. Questo è il motivo per cui non hai un'operazione di origine piano / riga nel tuo file tkprof. Sarebbe bello se si potesse ripetere il processo, se il suggerimento che segue risulta essere spazzatura.

Una piccola speculazione da parte mia: penso che DISTINCT sia quasi una no-op perché stai selezionando così tante colonne. Se questo è vero, allora il tuo predicato & Quot; WHERE STATUS_ID = 'ORDER_COMPLETED' & Quot; è molto selettivo e trarrai vantaggio dall'avere un indice su questa colonna. Dopo aver creato l'indice, assicurati di analizzarlo correttamente, magari anche con un istogramma attivo se i valori dei dati sono inclinati. Il risultato finale sarà un piano diverso per questa query, a partire da un INDICE RANGE SCAN, seguito da un TABLE ACCESS BY ROWID, che porta a una query molto veloce.

Dopo aver creato un indice, è possibile ripetere l'analisi della tabella, inclusi gli istogrammi utilizzando questa istruzione:

exec dbms_stats.gather_table_stats ([proprietario], [table_name], cascade = > true, method_opt = > 'FOR ALL INDEXED COLUMNS SIZE')

Saluti, Rob.

Altri suggerimenti

Dato che stai ordinando i risultati in base a order_date è importante avere un indice decrescente su quel campo. Inoltre, informa l'oracolo che desideri utilizzare questo indice. Posiziona prima il campo <=> nella query e usa un suggerimento come

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

Non si tratta tanto di avere indici, ma piuttosto di dire all'oracolo di usarli. Oracle è molto esigente riguardo agli indici. Ottieni i migliori risultati quando scegli gli indici in base alle tue query più importanti. In caso di dubbi, traccia una query . In questo modo puoi vedere in quale parte dell'oracolo della query trascorre la maggior parte del tempo e se i tuoi indici sono effettivamente raccolti o meno. La traccia è preziosa quando si combattono i problemi di prestazioni.

ORDER_ID viene dichiarato come PK utilizzando un vincolo PRIMARY KEY? Perché se è così mi aspetterei che l'ottimizzatore riconosca che DISTINCT è superfluo in questa query e lo ottimizza. Senza il vincolo, non saprà che è superfluo e quindi spenderà sforzi inutili e considerevoli in & Quot; de-duping & Quot; i risultati.

Prova a disabilitare l'aggregazione hash:

select /*+ no_use_hash_aggregation*/ distinct ...

http://oracle-randolf.blogspot.com/2011/ 01 / hash-aggregation.html

Durante la risoluzione dei problemi delle applicazioni in cui non ho il controllo di SQL, trovo che il pacchetto dbms_sqltune fa risparmiare molto tempo. Vedi http://download.oracle.com/docs /cd/B28359_01/appdev.111/b28419/d_sqltun.htm e sì, sfortunatamente dovresti avere la licenza per usarlo.

Esistono procedure in questo pacchetto per eseguire un'analisi di ottimizzazione su un sql_id specifico nel pool condiviso o nel repository AWR. L'analisi conterrà raccomandazioni di indicizzazione in caso di miglioramenti da apportare con indici aggiuntivi. Ancora più importante, l'analizzatore potrebbe scoprire un percorso di accesso migliorato che può implementare con ciò che Oracle chiama un profilo SQL: si tratta di un insieme di suggerimenti che verranno archiviati e utilizzati ogni volta che viene eseguito sql_id. Ciò accade senza che sia necessario codificare i suggerimenti nell'istruzione SQL e esiste anche un'opzione per fare ciò che si può pensare come corrispondenza fuzzy se l'applicazione genera valori letterali nell'istruzione anziché variabili bind.

Ovviamente questo strumento non dovrebbe essere un sostituto per comprendere l'applicazione e le sue strutture di dati, ma leggere l'output che dettaglia il percorso di esecuzione di un piano migliore (se trovato) può essere educativo.

Oracle accede all'intera tabella ogni volta che si esegue la query (TABLE ACCESS (FULL)). Creazione di un INDICE sulle colonne STATUS_ID e ORDER_TYPE_ID

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

sarà di grande aiuto, soprattutto se ci sono diversi valori di STATUS_ID e ORDER_TYPE_ID nella tabella ORDER_HEADER.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top