2013-05-25 28 views
12

Ho 10 tabelle con la stessa struttura eccetto il nome della tabella.La stored procedure mysql è più lenta 20 volte rispetto alla query standard

ho una sp (stored procedure) definito come segue:

select * from table1 where (@param1 IS NULL OR [email protected]) 
UNION ALL 
select * from table2 where (@param1 IS NULL OR [email protected]) 
UNION ALL 
... 
... 
UNION ALL 
select * from table10 where (@param1 IS NULL OR [email protected]) 

che io chiamo la SP con la seguente riga:

call mySP('test') //it executes in 6,836s 

Poi ho aperto una nuova finestra di query standard. Ho appena copiato la query sopra. Quindi sostituito @ param1 con 'test'.

Questo eseguito in 0,321s ed è circa 20 volte più veloce della stored procedure.

Ho modificato ripetutamente il valore del parametro per impedire che il risultato sia memorizzato nella cache. Ma questo non ha cambiato il risultato. L'SP è circa 20 volte più lento della query standard equivalente.

Per favore, puoi aiutarmi a capire perché questo sta accadendo?

Qualcuno ha riscontrato problemi simili?

Sto usando mySQL 5.0.51 su Windows Server 2008 R2 64 bit.

modifica: sto utilizzando Navicat per il test.

Qualsiasi idea sarà utile per me.

Edit1:

Ho solo fatto qualche prova secondo la risposta di Barmar.

Al infine ho cambiato sp simili sottostante con una sola fila:

SELECT * FROM table1 WHERE [email protected] AND [email protected] 

Poi innanzi tutto eseguita la query standart

SELECT * FROM table1 WHERE col1='test' AND col2='test' //Executed in 0.020s 

Dopo ho chiamato il mio sp:

CALL MySp('test','test') //Executed in 0.466s 

Quindi ho cambiato completamente la clausola, ma nulla è cambiato. E ho chiamato la sp dalla finestra di comando mysql invece di navicat. Ha dato lo stesso risultato. Sono ancora bloccato su di esso.

mia sp DDL:

CREATE DEFINER = `myDbName`@`%` 
PROCEDURE `MySP` (param1 VARCHAR(100), param2 VARCHAR(100)) 
BEGIN 
    SELECT * FROM table1 WHERE col1=param1 AND col2=param2 
END 

E col1 e col2 è combinati indicizzati.

Si potrebbe dire che non usi la query standard? Il mio software design non è adatto a questo. Devo usare la stored procedure. Quindi questo problema è molto importante per me.

Edit2:

ho ottenuto di query informazioni del profilo. La grande differenza è dovuta alla "trasmissione della riga di dati" in SP Profile Information. L'invio di parte dati richiede% 99 del tempo di esecuzione della query. Sto facendo test sul server di database locale. Non mi sto connettendo dal computer remoto.

Informazioni SP Profilo SP Profile Information

Informazioni Query Profilo enter image description here

ho cercato INDEX forza come qui di seguito nella mia sp. Ma lo stesso risultato.

SELECT * FROM table1 FORCE INDEX (col1_col2_combined_index) WHERE [email protected] AND [email protected] 

Ho cambiato sp come di seguito.

EXPLAIN SELECT * FROM table1 FORCE INDEX (col1_col2_combined_index) WHERE col1=param1 AND col2=param2 

Questo ha dato questo risultato:

id:1 
select_type=SIMPLE 
table:table1 
type=ref 
possible_keys:NULL 
key:NULL 
key_len:NULL 
ref:NULL 
rows:292004 
Extra:Using where 

allora ho eseguito la query di seguito.

EXPLAIN SELECT * FROM table1 WHERE col1='test' AND col2='test' 

risultato è:

id:1 
select_type=SIMPLE 
table:table1 
type=ref 
possible_keys:col1_co2_combined_index 
key:col1_co2_combined_index 
key_len:76 
ref:const,const 
rows:292004 
Extra:Using where 

Sto usando dichiarazione Force Index in SP. Ma insiste nel non usare l'indice. Qualche idea? Penso di essere vicino alla fine :)

+1

Potrebbe essere che dopo aver eseguito l'SP, MySQL abbia memorizzato il risultato nella cache, e quindi quando lo si esegue all'esterno dell'SP, sta semplicemente colpendo la cache anziché eseguirla di nuovo. –

+0

A proposito, perché 10 tavoli con la stessa struttura? Perché non combinarli in 1 tavolo? –

+0

la progettazione del database è fuori mano non farei mai una tale progettazione :) prima eseguo la query con parametri diversi quindi immediatamente chiamo lo sp con lo stesso parametro. risultato uguale. sembra che SP non abbia nemmeno preso il risultato dalla cache. – bselvan

risposta

4

Possibile problema con il set di caratteri? Se il set di caratteri della tabella è diverso dal set di caratteri del database, ciò potrebbe causare un problema.

Vai a questa segnalazione di bug: http://bugs.mysql.com/bug.php?id=26224

[12 Nov 2007 21:32] Mark Kubacki Ancora nessuna fortuna con 5.1.22_rc - i tasti vengono memorizzati, la query viene eseguita in una procedura 36 secondi e all'esterno di 0.12s.

[12 Nov 2007 22:30] Mark Kubacki Dopo aver cambiato i set di caratteri UTF-8 (in particolare per i due utilizzato), che viene utilizzato per la connessione in ogni modo, le chiavi vengono presi in considerazione all'interno della stored procedure !

La domanda non posso rispondere è: Perché l'ottimizzatore trattamento charset conversioni un altro modo all'interno e al di fuori delle procedure memorizzate? (In effetti, potrei sbagliarlo a chiedere questo.)

+0

grazie mille. La ragione è esattamente la stessa. Il mio set di caratteri di tabella e database era diverso. Ho cambiato entrambi, quindi l'indice è stato usato in sp. – bselvan

8

Solo una supposizione:

Quando si esegue la query a mano, l'espressione WHERE ('test' IS NULL or COL1 = 'test') può essere ottimizzato quando la query viene analizzato. Il parser può vedere che la stringa 'test' non è nulla, quindi converte il test in WHERE COL1 = 'test'. E se c'è un indice su COL1 questo sarà usato.

Tuttavia, quando si crea una stored procedure, l'analisi si verifica quando viene creata la procedura. A quel tempo, non sa cosa sarà @param e dovrà implementare la query come scansione sequenziale della tabella.

Provare a modificare la procedura per:

IF @param IS NULL 
THEN BEGIN 
    SELECT * FROM table1 
    UNION ALL 
    SELECT * FROM table2 
    ... 
END; 
ELSE BEGIN 
    SELECT * FROM table1 WHERE col1 = @param 
    UNION ALL 
    SELECT * FROM table2 WHERE col1 = @param 
    ... 
END; 
END IF; 

non ho molta esperienza con le procedure stored MySQL, quindi non sono sicuro che sia tutta la sintassi corretta.

+0

Grazie per la risposta. Darò una prova. In realtà la mia clausola in cui non è così breve. (@ param1 è nullo o col1 = @ param1) e (@ param2 è nullo o col2 = @ param2) e .... a param6. Per essere chiari, non ho scritto così dettagliatamente. Penso che in questa situazione la tua soluzione non copra esattamente la mia domanda. – bselvan

+0

La differenza deriva dall'uso di variabili come '@Var è null o col1 = @ var'. Esegui EXPLAIN su entrambi: 'EXPLAIN SELECT * FROM MY_VIEW' e' SELECT * FROM SP_MY_PROC'. Dovrebbe dare i motivi per cui. In SQL Server è presente l'opzione RECOMPILE da utilizzare in questi casi per ricompilare il piano di esecuzione. Sto cercando ma non trovo qualcosa di simile in MySQL. – Stoleg

+0

@Stoleg Questa è stata la mia spiegazione originale, ma si è sbarazzato del 'OR' nella sua ultima versione. Quindi non so perché l'SP sarebbe ancora lento. – Barmar

0

Interessante domanda, perché mi piace usare le stored procedure. La ragione è la manutenzione e il principio di incapsulamento.

Si tratta di informazioni che ho trovato: http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html

Essa afferma che la cache delle query non viene utilizzato per le query che 1. sono una sottoquery che appartengono a una query esterna, e 2. vengono eseguiti all'interno del corpo di una stored procedure, trigger o evento.

Ciò significa che funziona come progettato.

0

Avevo visto questo comportamento, ma non era correlato al set di caratteri.

Avevo un tavolo con dati gerarchici autoreferenziali (un genitore con figli e alcuni bambini con figli propri, ecc.). Poiché parent_id doveva fare riferimento all'id principale (e la colonna specificava un vincolo a tale effetto), non potevo impostare l'id genitore su NULL o 0 (zero) per dissociare un bambino da un genitore, quindi l'ho semplicemente fatto riferimento a si.

Quando sono andato a eseguire una stored procedure per eseguire la query ricorsiva per trovare tutti i bambini (a tutti i livelli) di un particolare genitore, la query ha richiesto tra il 30 & 40 volte il tempo di esecuzione. Ho scoperto che modificare la query utilizzata dalla stored procedure per assicurarsi che escludesse il record padre di livello superiore (specificando WHERE parent_id! = Id) ripristinava le prestazioni della query.

La stored procedure che sto utilizzando è basato su quella mostrata in: https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql.

Problemi correlati