2011-10-30 13 views
51

Sto eseguendo alcuni test su un server HSQLDB con una tabella contenente 500.000 voci. La tabella non ha indici. Ci sono 5000 chiavi aziendali distinte. Ho bisogno di una lista di loro. Naturalmente ho iniziato con una query DISTINCT:Enorme differenza di prestazioni quando si utilizza il gruppo rispetto al distinto

SELECT DISTINCT business_key FROM memory WHERE 
    concept <> 'case' or 
    attrib <> 'status' or 
    value <> 'closed' 

si impiegano circa 90 secondi !!!

Poi ho provato ad utilizzare GROUP BY:

SELECT business_key FROM memory WHERE 
     concept <> 'case' or 
     attrib <> 'status' or 
     value <> 'closed' 
GROUP BY business_key 

e ci vuole 1 secondo !!!

Cercando di capire la differenza ho eseguito EXLAIN PLAN FOR ma sembra dare le stesse informazioni per entrambe le query.

EXLAIN PLAN FOR DISTINCT ...

isAggregated=[false] 
columns=[ 
    COLUMN: PUBLIC.MEMORY.BUSINESS_KEY 
] 
[range variable 1 
    join type=INNER 
    table=MEMORY 
    alias=M 
    access=FULL SCAN 
    condition = [ index=SYS_IDX_SYS_PK_10057_10058 
    other condition=[ 
    OR arg_left=[ 
    OR arg_left=[ 
     NOT_EQUAL arg_left=[ 
     COLUMN: PUBLIC.MEMORY.CONCEPT] arg_right=[ 
     VALUE = case, TYPE = CHARACTER]] arg_right=[ 
     NOT_EQUAL arg_left=[ 
     COLUMN: PUBLIC.MEMORY.ATTRIB] arg_right=[ 
     VALUE = status, TYPE = CHARACTER]]] arg_right=[ 
    NOT_EQUAL arg_left=[ 
     COLUMN: PUBLIC.MEMORY.VALUE] arg_right=[ 
     VALUE = closed, TYPE = CHARACTER]]] 
    ] 
]] 
PARAMETERS=[] 
SUBQUERIES[] 
Object References 
PUBLIC.MEMORY 
PUBLIC.MEMORY.CONCEPT 
PUBLIC.MEMORY.ATTRIB 
PUBLIC.MEMORY.VALUE 
PUBLIC.MEMORY.BUSINESS_KEY 
Read Locks 
PUBLIC.MEMORY 
WriteLocks 

EXLAIN PLAN FOR SELECT ... GROUP BY ...

isDistinctSelect=[false] 
isGrouped=[true] 
isAggregated=[false] 
columns=[ 
    COLUMN: PUBLIC.MEMORY.BUSINESS_KEY 
] 
[range variable 1 
    join type=INNER 
    table=MEMORY 
    alias=M 
    access=FULL SCAN 
    condition = [ index=SYS_IDX_SYS_PK_10057_10058 
    other condition=[ 
    OR arg_left=[ 
    OR arg_left=[ 
     NOT_EQUAL arg_left=[ 
     COLUMN: PUBLIC.MEMORY.CONCEPT] arg_right=[ 
     VALUE = case, TYPE = CHARACTER]] arg_right=[ 
     NOT_EQUAL arg_left=[ 
     COLUMN: PUBLIC.MEMORY.ATTRIB] arg_right=[ 
     VALUE = status, TYPE = CHARACTER]]] arg_right=[ 
    NOT_EQUAL arg_left=[ 
     COLUMN: PUBLIC.MEMORY.VALUE] arg_right=[ 
     VALUE = closed, TYPE = CHARACTER]]] 
    ] 
]] 
groupColumns=[ 
COLUMN: PUBLIC.MEMORY.BUSINESS_KEY] 
PARAMETERS=[] 
SUBQUERIES[] 
Object References 
PUBLIC.MEMORY 
PUBLIC.MEMORY.CONCEPT 
PUBLIC.MEMORY.ATTRIB 
PUBLIC.MEMORY.VALUE 
PUBLIC.MEMORY.BUSINESS_KEY 
Read Locks 
PUBLIC.MEMORY 
WriteLocks 

EDIT: ho fatto ulteriori test. Con 500.000 record in HSQLDB con tutte le chiavi aziendali distinte, le prestazioni di DISTINCT ora sono migliori - 3 secondi, rispetto allo GROUP BY che ha richiesto circa 9 secondi.

In MySQL entrambe le query preforma stessa:

MySQL: 500 000 righe - 5 000 distinte chiavi business: Entrambe le query: 0,5 secondi MySQL: 500 000 righe - tutte distinte chiavi business: SELECT DISTINCT ... - 11 secondi SELECT ... GROUP BY business_key - 13 secondi

Quindi il problema è relativo solo a HSQLDB.

Sarò molto grato se qualcuno può spiegare perché c'è una differenza così drastica.

+2

Si prega di mostrare il risultato di "ESPLINA PIANO' E provare a eseguire la' DISTINCT'query dopo aver eseguito il 'GROUP BY' per vedere se forse un po 'di cache sta distorcendo i tempi ... – Yahia

+0

Dato che si ottiene lo stesso piano per ogni query, sembra che i dati della tabella o il risultato sia stato memorizzato nella cache. –

+0

Li ho eseguiti così tante volte che credono che il caching non sia un problema. Sto postando l'output di 'EXLAIN PLAN FOR'. –

risposta

54

Le due domande esprimono la stessa domanda. Apparentemente il Query Optimizer sceglie due diversi piani di esecuzione.La mia ipotesi è che l'approccio distinct viene eseguito come:

  • Copiare tutti business_key i valori in una tabella temporanea
  • Ordinare la tabella temporanea
  • scansione la tabella temporanea, tornando ogni elemento che è diverso da quello prima che

Il group by potrebbe essere eseguito come:

  • Scansione tavolo pieno, memorizzando ogni valore business key in una tabella hash
  • ritorno delle chiavi della tabella hash

Il primo metodo ottimizza per l'utilizzo della memoria: sarebbe comunque eseguire ragionevolmente bene quando parte della tabella temporanea deve essere scambiato. Il secondo metodo ottimizza la velocità, ma potenzialmente richiede una grande quantità di memoria se ci sono molte chiavi diverse.

Poiché si dispone di una memoria sufficiente o di pochi tasti diversi, il secondo metodo ha prestazioni migliori rispetto al primo. Non è raro vedere differenze di prestazioni di 10x o anche di 100x tra due piani di esecuzione.

+0

Grazie per la risposta. Le tue ipotesi sono evidenti dall'output di 'EXPLAIN'? Entrambi hanno lo stesso aspetto per me. –

+0

Per quanto posso vedere, il piano non specifica come eseguirà il join. Non sono nemmeno sicuro del motivo per cui dovrebbe eseguire un join. Probabilmente serve uno specialista HSQLDB per leggere l'output di spiegare. – Andomar

+0

Come indica la risposta, il secondo metodo utilizza più memoria e potrebbe colpire troppo spesso la garbage collection (GC). Se si aumenta l'allocazione della memoria JVM, non ci dovrebbe essere un'enorme differenza tra i due tempi di interrogazione. – fredt

Problemi correlati