2016-07-18 9 views
1

Ich habe diese Arbeit SQL-Abfrage, aber es macht mich fast DB Absturz:Eine korrekte MySQL Left Join-Abfrage überfordert eine smallsized DB obwohl Zeilen indiziert sind

SELECT MASTER.master_id, 
     MASTER.master_summary, 
     MASTER.master_start, 
     MASTER.master_end, 
     MASTER.master_risk, 
     MASTER.master_source, 
     MASTER.master_veto, 
     master.master_tags, 
     NULL AS HAS_CE, 
     C2C.c2c_customer 
FROM  `cer_master` MASTER 
LEFT JOIN `cer_c2customer` C2C 
       ON (C2C.c2c_id = MASTER.master_id AND C2C.c2c_source = MASTER.master_source) 

WHERE (MASTER.master_id NOT LIKE 'TAV%') 
     AND ((MASTER.master_class <> 'type2') OR (MASTER.master_class <> 'type3')) 
     AND (MASTER.master_status <> 'Cancelled') 
     AND (MASTER.master_end >= Now() AND MASTER.master_start >= Date_sub(Now(), INTERVAL 1 day)) 

Wenn ich versuche, dies auf phpMyAdmin zu laufen habe ich buchstäblich zu warten auf 5min und dieses Ergebnis: 3699 insgesamt, Query 0,9358 sec nahm

I MASTER.master_id, MASTER.master_start, MASTER.master_end, MASTER.master_source aswell als c2c.c2c_id, C2C.c2c_source and C2C.c2c_customer indiziert haben, aber es scheint nicht zu helfen.

Zusätzliche Informationen: cer_master MASTER Tabelle hat 277.502 Zeilen und cer_c2customer C2C Tabelle hat 72.788 Zeilen.

Kann mir jemand helfen, diese Abfrage zu optimieren? Ich brauche es dringend und kann mir keinen anderen Weg vorstellen.

EDIT: Die Ergebnisse der EXPLAIN Abfrage:

+----+-------------+--------+-------+-------------------------------------------------------+---------------------------------+---------+------+-------+-------------+ 
| id | select_type | table | type | possible_keys           | key        | key_len | ref | rows | Extra  | 
+----+-------------+--------+-------+-------------------------------------------------------+---------------------------------+---------+------+-------+-------------+ 
| 1 | SIMPLE  | MASTER | range | CHM_MASTER_SCHEDULED_START_DATE,CHM_MASTER_SCHEDUL... | CHM_MASTER_SCHEDULED_START_DATE | 4  | NULL | 5042 | Using where | 
+----+-------------+--------+-------+-------------------------------------------------------+---------------------------------+---------+------+-------+-------------+ 
| 1 | SIMPLE  | C2C | ALL | CER_C2C_CHANGE_ID          | NULL       | NULL | NULL | 72788 |    | 
+----+-------------+--------+-------+-------------------------------------------------------+---------------------------------+---------+------+-------+-------------+ 

Table Create Table 
master CREATE TABLE `master` (
`ID` int(11) NOT NULL AUTO_INCREMENT, 
` MASTER_LAST_MODIFIED_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
` MASTER_SOURCE` varchar(16) NOT NULL, 
` MASTER_ID` varchar(16) NOT NULL, 
` MASTER_SUMMARY` text NOT NULL, 
` MASTER_NOTES` text NOT NULL, 
` MASTER_SERVICE` varchar(255) NOT NULL, 
` MASTER_SITE` text NOT NULL, 
` MASTER_START` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
` MASTER_END` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
` MASTER_DEPARTMENT_FLAG` varchar(8) NOT NULL, 
` MASTER_RISK` int(8) NOT NULL DEFAULT '1', 
` MASTER_IMPACT_LEVEL` varchar(64) NOT NULL, 
` MASTER_TOOL_STATUS` varchar(32) NOT NULL, 
` MASTER_IMPACT_RISK_NOTES` text NOT NULL, 
` MASTER_CALENDAR_WEEK` varchar(16) NOT NULL, 
` MASTER_TAGS` varchar(1024) NOT NULL, 
` MASTER_VETO` tinyint(1) NOT NULL DEFAULT '0', 
` MASTER_LAYER_TAGS` text NOT NULL, 
` MASTER_ORAKEL_ID` int(11) NOT NULL DEFAULT '0', 
` MASTER_USED_TEMPLATE` text NOT NULL, 
PRIMARY KEY (`ID`), 
KEY ` MASTER_ID` (` MASTER_CHANGE_ID`), 
KEY ` MASTER_LAST_MODIFIED_DATE` (` MASTER_LAST_MODIFIED_DATE`), 
KEY ` MASTER_SERVICE` (` MASTER_SERVICE`), 
KEY ` MASTER_START` (` MASTER_START`), 
KEY ` MASTER_END` (` MASTER_END_`), 
KEY ` MASTER_SOURCE` (` MASTER_SOURCE`) 
) ENGINE=MyISAM AUTO_INCREMENT=278315 DEFAULT CHARSET=utf8 

und das ist Show-Tabelle aus c2c_customer erstellen: cerberus_change2customer

CREATE TABLE `c2c_customer` (
`ID` int(11) NOT NULL AUTO_INCREMENT, 
`C2C_SOURCE` text NOT NULL, 
`C2C_ID` text NOT NULL, 
`C2C_CUSTOMER` text NOT NULL, 
`C2C_LAST_MODFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
PRIMARY KEY (`ID`), 
FULLTEXT KEY `C2C_ID` (`C2C_ID`), 
FULLTEXT KEY `C2C_CUSTOMER` (`C2C_CUSTOMER`) 
) ENGINE=MyISAM AUTO_INCREMENT=516044 DEFAULT CHARSET=utf8 
+0

Welche Indizes haben Sie verwendet? Was ist Ihr Clustered-Index? – Ben

+0

Sie müssen Ihre erklären – e4c5

+0

@Ben Ich fürchte, ich bin kein Experte für die Indizierung, alles, was ich tat ging in 'Struktur'-Ansicht der db und klicken Sie dann auf 'add index'on der Spalte aus der' Mehr-Menü. Kann ich das anders/effizienter machen? – sardine

Antwort

1

Ihr Index für alle Spalten sein soll, nicht getrennten Indizes für jeden Säule.

Zum Beispiel:

ALTER TABLE `cer_c2customer` ADD INDEX `cer_c2customer_ID_SOURCE_CUSTOMER` (c2c_id, c2c_source, c2c_customer) 

Dies bedeutet, dass der ein Index verwendet werden kann, um die Daten zu lokalisieren und liefert auch alle Spalten aus dieser Tabelle in der Abfrage erforderlich.

Darüber hinaus möchten Sie wahrscheinlich, dass der Clusterindex auf cer_master das Startdatum oder das Enddatum sein soll.

+0

vielen Dank für die Hilfe. Ich hoffe, diese Nachricht hat Sie rechtzeitig erreicht, weil - ugh, ich weiß nicht weiter! Ich habe eine Kopie meiner Tabelle erstellt und getan, was Sie gesagt haben, geändert und den Index dieser drei Spalten hinzugefügt. Aber was soll ich jetzt tun? Kann ich meine alte Abfrage nur für diese kopierte Tabelle ausführen und sie sollte mit diesem neuen Index schneller laufen? Oder muss ich mich irgendwie darauf beziehen? – sardine

+0

Niemals! Es wirkt wie ein Zauber! Vielen Dank, die Abfrage läuft jetzt in ein paar Millisekunden! – sardine

+0

@sardine Stellen Sie sicher, dass dies nicht auf das Zwischenspeichern zurückzuführen ist, indem Sie das Schlüsselwort SQL_NO_CACHE einschließen. – Strawberry

1

Ein Trick ist, Ihre where-Klausel richtig

WHERE (MASTER.master_id NOT LIKE 'TAV%') 
     AND ((MASTER.master_class <> 'type2') OR (MASTER.master_class <> 'type3')) 
     AND (MASTER.master_status <> 'Cancelled') 
     AND (MASTER.master_end >= Now() AND MASTER.master_start >= Date_sub(Now(), INTERVAL 1 day)) 

Bewegen Sie den oben where-Klausel, um zu bestellen, die am schnellsten und hat die samllest Ergebnismenge.

Zum Beispiel, wenn Sie diese Bedingung wissen, hat es kleine Ergebnismenge

(MASTER.master_status <> 'Cancelled') 

So nach oben, die Abfrage wird

WHERE (MASTER.master_status <> 'Cancelled') 
     AND ((MASTER.master_class <> 'type2') OR (MASTER.master_class <> 'type3')) 
     AND (MASTER.master_id NOT LIKE 'TAV%') 
     AND (MASTER.master_end >= Now() AND MASTER.master_start >= Date_sub(Now(), INTERVAL 1 day)) 
+0

Die Reihenfolge der Klauseln in der WHERE-Klausel ist nicht wirklich wichtig. Der Optimierer wählt basierend auf Statistiken einen Index (oder keinen Index) aus; Dies ist bei weitem die wichtigste Leistung. Dann wird der Rest des WHERE für jede noch nicht ausgefilterte Zeile ausgewertet. An diesem Punkt ist die Reihenfolge wichtig, aber es ist nicht signifikant genug, um von Bedeutung zu sein. –

1

In der Reihenfolge ihrer Bedeutung:

  1. Fix die OR das ist immer WAHR. (Bugfix, nicht Optimierung)
  2. Bens Composite (und Covering) Index hinzufügen.
  3. Wechseln Sie zu InnoDB.
  4. INDEX(master_start), INDEX(master_end) - Ja, zwei separate, nicht eine zusammengesetzte, Index. Der Optimierer kann einen von ihnen auswählen und profitieren.
  5. Bestellen Sie die WHERE Klausel, wie Shikhar vorschlägt. (Selbst nach dem Rest wird dies nur eine unbedeutende Verbesserung liefern.)