2015-05-21 4 views
5

Ich habe eine SQL-Anfrage, die 100% meiner VM-CPU übernehmen, während es funktioniert. Ich möchte wissen, wie es zu optimieren:SQL-Anfrage Optimierung

SELECT g.name AS hostgroup 
    , h.name AS hostname 
    , a.host_id 
    , s.display_name AS servicename 
    , a.service_id 
    , a.entry_time AS ack_time 
    , ( SELECT ctime 
      FROM logs 
      WHERE logs.host_id = a.host_id 
      AND logs.service_id = a.service_id 
      AND logs.ctime < a.entry_time 
      AND logs.status IN (1, 2, 3) 
      AND logs.type = 1 
      ORDER BY logs.log_id DESC 
      LIMIT 1) AS start_time 
    , ar.acl_res_name AS timeperiod 
    , a.state AS state 
    , a.author 
    , a.acknowledgement_id AS ack_id 
FROM centstorage.acknowledgements a 
LEFT JOIN centstorage.hosts h ON a.host_id = h.host_id 
LEFT JOIN centstorage.services s ON a.service_id = s.service_id 
LEFT JOIN centstorage.hosts_hostgroups p ON a.host_id = p.host_id 
LEFT JOIN centstorage.hostgroups g ON g.hostgroup_id = p.hostgroup_id 
LEFT JOIN centreon.hostgroup_relation hg ON a.host_id = hg.host_host_id 
LEFT JOIN centreon.acl_resources_hg_relations hh ON hg.hostgroup_hg_id = hh.hg_hg_id 
LEFT JOIN centreon.acl_resources ar ON hh.acl_res_id = ar.acl_res_id 
WHERE ar.acl_res_name != 'All Resources' 
AND YEAR(FROM_UNIXTIME(a.entry_time)) = YEAR(CURDATE()) 
AND MONTH(FROM_UNIXTIME(a.entry_time)) = MONTH(CURDATE()) 
AND a.service_id is not null 
ORDER BY a.acknowledgement_id ASC 

Das Problem ist in diesem Teil:

(SELECT ctime FROM logs 
WHERE logs.host_id = a.host_id 
    AND logs.service_id = a.service_id 
    AND logs.ctime < a.entry_time 
    AND logs.status IN (1, 2, 3) 
    AND logs.type = 1 
ORDER BY logs.log_id DESC 
LIMIT 1) AS start_time 

Die Tabellenprotokolle sind wirklich riesig und einige Freunde sagten mir, eine Puffertabelle/Datenbank zu verwenden, aber ich ziemlich wusste diese Dinge und ich weiß nicht, wie es geht.

Es wird eine EXPLAIN der Abfrage VERLÄNGERT: Here !

Es scheint, dass er nur 2 Reihe der Tabellenprotokolle untersucht werden, so, warum es so viel Zeit in Anspruch nimmt? (In den Tabellenprotokollen befindet sich eine 560000 Zeile). Hier

ist alle Indizes dieser Tabellen:

centstorage.acknowledgements:

enter image description here centstorage.hosts:

enter image description here centstorage.services:

enter image description here centstorage.hosts_hostgroups:

enter image description here centstorage.hostgroups:

enter image description here centreon.hostgroup_relation:

enter image description here centreon.acl_resources_hg_relations:

enter image description here centreon.acl_resources:

enter image description here

+0

Welches dbms-Produkt? Tabellen- und Indexdefinitionen usw. – jarlh

+2

Überprüfen Sie zuerst den Ausführungsplan und vergewissern Sie sich, dass Ihnen ein Index fehlt. – Galma88

+0

Ich benutze MySQL. –

Antwort

0

Für SQL Server besteht die Möglichkeit, den maximalen Grad der Parallelität von Kriterien MAXDOP

Zum Beispiel definieren Sie am Ende Ihrer Anfrage

option (maxdop 2) 

Ich bin mir ziemlich sicher, dass es eine definieren gleichwertig in MySql.

Sie können versuchen, sich dieser Situation zu nähern, wenn die Ausführungszeit nicht relevant ist.

+1

gibt es keine solche Sache für mysql, nicht ohne für Drittanbieter-Add-ons gehen –

0
  1. Erstellen einer temporären Tabelle von dem die Bedingung für Bestätigungen, wird Schema Spalte in Endergebnisses erforderlich sind und in JOIN mit allen 7 Tabellen

    CREATE TEMPORARY TABLE __tempacknowledgements AS SELECT g.name AS hostgroup 
        , '' AS hostname 
        , a.host_id 
        , s.display_name AS servicename 
        , a.service_id 
        , a.entry_time AS ack_time 
        , '' AS AS start_time 
        , '' AS timeperiod 
        , a.state AS state 
        , a.author 
        , a.acknowledgement_id AS ack_id 
    FROM centstorage.acknowledgements a 
    WHERE YEAR(FROM_UNIXTIME(a.entry_time)) = YEAR(CURDATE()) 
    AND MONTH(FROM_UNIXTIME(a.entry_time)) = MONTH(CURDATE()) 
    AND a.service_id IS NOT NULL 
    ORDER BY a.acknowledgement_id ASC; 
    

Oder erstellen Sie mit richtigen Spaltendefinition verwendet

  1. Aktualisiere Felder von allen Tabellen, die links beitreten, können Sie Inner Join in update verwenden. Sie sollten 7 verschiedene Update-Anweisungen schreiben. 2 Beispiele sind unten angegeben.

    UPDATE __tempacknowledgements a JOIN centstorage.hosts h USING(host_id) 
    SET a.name=h.name; 
    
    UPDATE __tempacknowledgements s JOIN centstorage.services h USING(service_id) 
    SET a.acl_res_name=s.acl_res_name; 
    
  2. ähnlich Update Ctime von Protokollen mit Logs mit Join, dann ist dies der 8. Update-Anweisung.

  3. Wählen Sie aus der Tempentabelle aus.
  4. Tropfen Temptabelle

ein sp kann für diese geschrieben werden.

+0

Scheint ein guter Weg, um mir zu helfen, aber ich bin immer noch ein Anfänger in SQL komplexen Abfragen .. Also ich bekomme es nicht alles –

+0

Einige Beispiel-Abfrage hinzugefügt, kann Ihnen helfen, die Idee über die vorgeschlagene Lösung zu bekommen. – Anil

0

Drehen Sie LEFT JOIN in JOIN, es sei denn, Sie haben eine echte Notwendigkeit für LEFT.

AND YEAR(FROM_UNIXTIME(a.entry_time)) = YEAR(CURDATE()) 
AND MONTH(FROM_UNIXTIME(a.entry_time)) = MONTH(CURDATE()) 
AND a.service_id is not null 

Haben Sie Zeilen mit a.service_id is not null? Wenn nicht, loswerden.

Wie bereits erwähnt, wird dieser Datumsvergleich nicht optimiert. Hier ist, was stattdessen zu verwenden:

AND a.entry_time >= CONCAT(LEFT(CURDATE(), 7), '-01') 
AND a.entry_time < CONCAT(LEFT(CURDATE(), 7), '-01') + INTERVAL 1 MONTH 

Und eine davon hinzufügen (abhängig von meinem obigen Kommentar):

INDEX(entry_time) 
INDEX(service_id, entry_time) 

Die korrelierte Unterabfrage ist schwer zu optimieren. Dieser Index (auf logs) kann helfen:

INDEX(type, host_id, service_id, status) 
0

WO IN ist Zeitkiller! Statt logs.status IN (1, 2, 3) Verwendung logs.status = 1 oder logs.status = 2 oder logs.status = 3

+0

Könntest du ein wenig näher ausführen? –

0

I SLIGHTLY die Abfrage neu formatiert haben meine Lesbarkeitsreferenz und besser die Beziehungen zwischen den Tabellen zu sehen ... ansonsten diesen Teil ignorieren.

SELECT 
     g.name AS hostgroup, 
     h.name AS hostname, 
     a.host_id, 
     s.display_name AS servicename, 
     a.service_id, 
     a.entry_time AS ack_time, 
     (SELECT 
       ctime 
      FROM 
       logs 
      WHERE 
        logs.host_id = a.host_id 
       AND logs.service_id = a.service_id 
       AND logs.ctime < a.entry_time 
       AND logs.status IN (1, 2, 3) 
       AND logs.type = 1 
      ORDER BY 
       logs.log_id DESC 
      LIMIT 1) AS start_time, 
     ar.acl_res_name AS timeperiod, 
     a.state AS state, 
     a.author, 
     a.acknowledgement_id AS ack_id 
    FROM 
     centstorage.acknowledgements a 
     LEFT JOIN centstorage.hosts h 
      ON a.host_id = h.host_id 
     LEFT JOIN centstorage.services s 
      ON a.service_id = s.service_id 
     LEFT JOIN centstorage.hosts_hostgroups p 
      ON a.host_id = p.host_id 
      LEFT JOIN centstorage.hostgroups g 
       ON p.hostgroup_id = g.hostgroup_id 
     LEFT JOIN centreon.hostgroup_relation hg 
      ON a.host_id = hg.host_host_id 
      LEFT JOIN centreon.acl_resources_hg_relations hh 
       ON hg.hostgroup_hg_id = hh.hg_hg_id 
       LEFT JOIN centreon.acl_resources ar 
        ON hh.acl_res_id = ar.acl_res_id 
    WHERE 
      ar.acl_res_name != 'All Resources' 
     AND YEAR(FROM_UNIXTIME(a.entry_time)) = YEAR(CURDATE()) 
     AND MONTH(FROM_UNIXTIME(a.entry_time)) = MONTH(CURDATE()) 
     AND a.service_id is not null 
    ORDER BY 
     a.acknowledgement_id ASC 

möchte ich zunächst empfehlen, mit Ihrem „Bestätigungen“ Tabelle beginnen und haben einen Index auf ein Minimum von (ENTRY_TIME, acknowledgement_id). Als nächstes aktualisieren Sie Ihre WHERE-Klausel. Da Sie eine Funktion ausführen, um den Unix-Zeitstempel in ein Datum zu konvertieren und das Jahr (bzw. den Monat) zu erfassen, glaube ich nicht, dass es den Index verwendet, da er dies für jede Zeile berechnen muss. Um dies zu erhöhen, ist ein Unix-Timestamp nichts anderes als eine Zahl, die Sekunden von einem bestimmten Zeitpunkt darstellt. Wenn Sie nach einem bestimmten Monat suchen, berechnen Sie die Start- und End-Unix-Zeiten vor und führen Sie diesen Bereich aus. Etwas wie...

und a.entry_time> = UNIX_TIMESTAMP ('2015.10.01') und a.entry_time < UNIX_TIMESTAMP ('2015.11.01')

Auf diese Weise es entfallen alle Sekunden innerhalb der Monat bis 11:59:59 am 31. Oktober, kurz vor dem 1. November.

Dann, ohne meine Brille mehr alle Bilder klar zu sehen, und heute Morgen kurze Zeit, ich würde sicherstellen, dass Sie zumindest die folgenden Indizes für jede Tabelle jeweils

table    index 
logs    (host_id, service_id, type, status, ctime, log_id) 
acknowledgements (entry_time, acknowledgement_id, host_id, service_id) 
hosts    (host_id, name) 
services   (service_id, display_name) 
hosts_hostgroups (host_id, hostgroup_id) 
hostgroups   (hostgroup_id, name) 
hostgroup_relation (host_host_id, hostgroup_hg_id) 
acl_resources_hg_relations (hh_hg_id, acl_res_id) 
acl_resources ar (acl_res_id, acl_res_name) 

Schließlich Ihre korrelierten sub- Abfrage-Feld wird ein Mörder sein, wie es für jede Zeile verarbeitet wird, aber hoffentlich helfen die anderen Indexoptimierung Ideen Leistung.