2013-05-25 35 views
12

Ich habe 10 Tabellen mit derselben Struktur außer Tabellenname.Mysql gespeicherte Prozedur ist 20 Mal langsamer als Standardabfrage

ich habe ein sp (Stored Procedure) wie folgt definiert:

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]) 

Ich bin den sp mit der folgenden Zeile Aufruf:

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

Dann öffnete ich ein neues Standard Abfragefenster. Ich habe nur die obige Abfrage kopiert. Dann @ param1 durch 'test' ersetzt.

Dies ist in 0,321 ausgeführt und ist etwa 20 mal schneller als die gespeicherte Prozedur.

Ich habe den Parameterwert wiederholt geändert, um zu verhindern, dass das Ergebnis zwischengespeichert wird. Aber das hat das Ergebnis nicht verändert. Der SP ist etwa 20 mal langsamer als die äquivalente Standardabfrage.

Bitte können Sie mir helfen, herauszufinden, warum das passiert?

Hat jemand auf ähnliche Probleme stoßen?

Ich benutze mySQL 5.0.51 auf Windows Server 2008 R2 64 Bit.

bearbeiten: Ich verwende Navicat für den Test.

Jede Idee wird für mich hilfreich sein.

EDIT1:

Ich habe gerade einige Test erfolgt nach Barmar Antwort.

Am endlich ich habe die sp wie unten mit einer nur einer Zeile geändert:

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

Dann zunächst ich die standart Abfrage ausgeführt

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

Nachdem ich das mein sp genannt:

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

So habe ich Where-Klausel vollständig geändert, aber nichts geändert. Und ich rief das sp vom mysql Befehlsfenster anstelle von navicat an. Es gab dasselbe Ergebnis. Ich bleibe immer noch dran.

meine sp ddl:

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

Und col1 und col2 indiziert kombiniert.

Sie könnten sagen, warum verwenden Sie nicht Standardabfrage dann? Mein Software-Design ist dafür nicht geeignet. Ich muss gespeicherte Prozedur verwenden. Dieses Problem ist mir sehr wichtig.

EDIT2:

Ich habe Informationen Abfrage Profil bekommen. Der große Unterschied liegt in der "Datenzeile senden" in den SP-Profilinformationen. Das Senden des Datenteils dauert% 99 der Ausführungszeit der Abfrage. Ich mache einen Test auf dem lokalen Datenbankserver. Ich verbinde keine Verbindung zum Remote-Computer.

SP Profil Informationen SP Profile Information

Abfrage Profil Informationen enter image description here

I wie unten Kraftindex-Anweisung versucht haben, in meinem sp. Aber das gleiche Ergebnis.

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

Ich habe SP wie unten geändert.

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

Dies gab dieses Ergebnis:

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

Dann habe ich die Abfrage unten ausgeführt.

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

Ergebnis ist:

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 

Ich FORCE INDEX-Anweisung in SP verwenden. Aber es besteht darauf, den Index nicht zu verwenden. Irgendeine Idee? Ich glaube, ich bin fast zu Ende :)

+1

Es könnte sein, dass MySQL nach dem Ausführen des SP das Ergebnis zwischengespeichert hat, und dann, wenn Sie es außerhalb des SP ausführen, trifft es nur den Cache, anstatt es erneut auszuführen. –

+0

Übrigens, warum 10 Tabellen mit der gleichen Struktur? Warum kombinieren Sie sie nicht in 1 Tabelle? –

+0

Datenbank-Design ist aus meiner Hand Ich würde nie so ein Design machen :) zuerst führe ich die Abfrage mit anderen Parameter dann sofort rufe ich die SP mit dem gleichen Parameter. Ergebnis gleich. Es scheint, dass sp nicht das Ergebnis vom Cache genommen hat. – bselvan

Antwort

4

Mögliches Problem mit Zeichensatz? Wenn sich Ihr Tabellenzeichensatz von Ihrem Datenbankzeichensatz unterscheidet, kann dies ein Problem verursachen.

Sehen Sie diesen Bug-Report: http://bugs.mysql.com/bug.php?id=26224

[12. November 2007 21.32] Mark Kubacki noch kein Glück mit 5.1.22_rc - Schlüssel sind inkorrekt, Abfrage dauert innerhalb einer Prozedur 36 Sekunden und außerhalb 0,12s.

[12. November 2007 22.30] Mark Kubacki verändert charsets auf UTF-8 Nachdem (vor allem für die beiden verwendeten), die für die Verbindung verwendet wird sowieso, Schlüssel Rechnung innerhalb der gespeicherten Verfahren genommen!

Die Frage, die ich nicht beantworten kann, ist: Warum behandelt der Optimierer charset Konvertierungen einen anderen Weg innerhalb und außerhalb gespeicherter Prozeduren? (In der Tat, ich könnte falsch sein, dies zu fragen.)

+0

vielen Dank. Der Grund ist genau der gleiche. Mein Tabellen- und Datenbankzeichensatz war anders. Ich habe beide gleich geändert, dann wurde der Index in SP verwendet. – bselvan

8

Nur eine Vermutung:

Wenn Sie die Abfrage von Hand ausgeführt werden, kann die Expression WHERE ('test' IS NULL or COL1 = 'test') optimiert werden, wenn die Abfrage analysiert wird. Der Parser kann erkennen, dass die Zeichenfolge 'test' nicht null ist. Daher konvertiert er den Test in WHERE COL1 = 'test'. Und wenn es einen Index auf COL1 gibt, wird dies verwendet.

Wenn Sie jedoch eine gespeicherte Prozedur erstellen, tritt das Parsen beim Erstellen der Prozedur auf. Zu diesem Zeitpunkt weiß es nicht, was @param sein wird, und muss die Abfrage als sequenziellen Scan der Tabelle implementieren.

Versuchen Sie Ihr Verfahren zum Ändern:

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; 

Ich habe nicht viel Erfahrung mit MySQL gespeicherte Prozeduren, so dass ich bin mir nicht sicher, dass alle die richtige Syntax ist.

+0

Danke für die Antwort. Ich werde es versuchen. Eigentlich ist meine Where-Klausel nicht so kurz. (@ param1 ist null oder col1 = @ param1) und (@ param2 ist null oder col2 = @ param2) und .... bis param6. Um klar zu sein, habe ich nicht so detailliert geschrieben. Ich denke, in dieser Situation deckt Ihre Lösung meine Frage nicht genau ab. – bselvan

+0

Der Unterschied kommt von der Verwendung von Variablen als @Var ist null oder col1 = @ var. Führen Sie EXPLAIN für beide aus: 'EXPLAIN SELECT * FROM MY_VIEW' und' SELECT * FROM SP_MY_PROC'. Es sollte die Gründe dafür geben. In SQL Server gibt es und Option RECOMPILE, um in solchen Fällen den Ausführungsplan neu zu kompilieren. Ich suche, aber nicht in MySQL so etwas zu finden. – Stoleg

+0

@Stoleg Das war meine ursprüngliche Erklärung, aber er hat das OR in seiner neuesten Version losgeworden. Ich weiß also nicht, warum der SP immer noch langsam wäre. – Barmar

0

Interessante Frage, weil ich gerne gespeicherte Prozeduren verwende. Grund ist Wartung und das Verkapselungsprinzip. Diese

ist Information, die ich gefunden: http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html

Es besagt, dass die Abfrage-Cache nicht für Abfragen verwendet wird, dass 1. eine Unterabfrage, die zu einer äußeren Abfrage gehört, und 2. innerhalb des Körpers ausgeführt einer gespeicherten Prozedur, eines Triggers oder eines Ereignisses.

Dies bedeutet, dass es wie vorgesehen funktioniert.

0

ich dieses Verhalten gesehen hatte, aber es war nicht auf den Zeichensatz verwendet.

Ich hatte eine Tabelle, die sich selbst verweisende hierarchische Daten (ein Elternteil mit Kindern, und einige Kinder hatten eigene Kinder, etc.). Da die parent_id auf die primäre ID verweisen musste (und die Spalte eine Einschränkung für diesen Effekt angegeben hat), konnte ich die übergeordnete ID nicht auf NULL oder 0 (null) setzen, um die Zuordnung eines untergeordneten Objekts von einem übergeordneten Element aufzuheben selbst.

Wenn ich eine gespeicherte Prozedur ausführen, um die rekursive Abfrage zu finden, um alle untergeordneten Elemente (auf allen Ebenen) eines bestimmten übergeordneten Elements zu finden, dauerte die Abfrage zwischen 30 & 40 mal so lange zu laufen. Ich fand, dass das Ändern der Abfrage, die von der gespeicherten Prozedur verwandt wird, um sicherzustellen, dass es den übergeordneten übergeordneten Datensatz (durch Angabe von WHERE parent_id! = Id) ausschloss, die Leistung der Abfrage wiederherstellte. Die gespeicherte Prozedur, die ich verwende, basiert auf der folgenden Abbildung: https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql.

Verwandte Themen