2012-10-23 10 views
8

Dies fühlt sich an wie eine "mach meine Hausaufgaben für mich" Art von Frage, aber ich bin wirklich hier stecken zu versuchen, diese Abfrage schnell gegen eine Tabelle mit vielen vielen Zeilen ausführen. Here's a SQLFiddle, die das Schema (mehr oder weniger) zeigt.Abfrage für Tabelle mit Hunderten von Millionen von Zeilen optimieren

Ich habe mit den Indizes gespielt und versucht, etwas zu bekommen, das alle erforderlichen Spalten zeigen wird, aber nicht viel Erfolg gehabt habe. Hier ist die create:

CREATE TABLE `AuditEvent` (
    `auditEventId` bigint(20) NOT NULL AUTO_INCREMENT, 
    `eventTime` datetime NOT NULL, 
    `target1Id` int(11) DEFAULT NULL, 
    `target1Name` varchar(100) DEFAULT NULL, 
    `target2Id` int(11) DEFAULT NULL, 
    `target2Name` varchar(100) DEFAULT NULL, 
    `clientId` int(11) NOT NULL DEFAULT '1', 
    `type` int(11) not null, 
    PRIMARY KEY (`auditEventId`), 
    KEY `Transactions` (`clientId`,`eventTime`,`target1Id`,`type`), 
    KEY `TransactionsJoin` (`auditEventId`, `clientId`,`eventTime`,`target1Id`,`type`) 
) 

Und (eine Version) die select:

select ae.target1Id, ae.type, count(*) 
from AuditEvent ae 
where ae.clientId=4 
    and (ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00') 
group by ae.target1Id, ae.type; 

ich am Ende mit einem 'Verwenden von temporär' und 'Verwenden von filesort' auch. Ich habe versucht, die count(*) fallen zu lassen und select distinct stattdessen zu verwenden, die den "Using filesort" nicht verursacht. Dies wäre wahrscheinlich in Ordnung, wenn es einen Weg zu join zurück geben würde, um die Zählimpulse zu erhalten.

Ursprünglich wurde die Entscheidung getroffen, target1Name und target2Name der Ziele so zu verfolgen, wie sie bei der Erstellung des Prüfdatensatzes vorhanden waren. Ich brauche diese Namen auch (die neuesten werden es tun).

Derzeit läuft die Abfrage (oben, mit fehlenden spalten target1Name und target2Name) in etwa 5 Sekunden auf ~ 24 Millionen Datensätze. Unser Ziel liegt bei Hunderten von Millionen und wir möchten, dass die Abfrage weiterhin in diese Richtung geht (in der Hoffnung, es unter 1-2 Minuten zu halten, aber wir hätten es gerne viel besser), aber meine Angst ist einmal Wir treffen diese größere Menge an Daten, die es nicht tun wird (es wird daran gearbeitet, zusätzliche Zeilen zu simulieren).

Ich bin mir nicht sicher, die beste Strategie, um die zusätzlichen Felder zu bekommen. Wenn ich die Spalten direkt in die select hinzufügen, verliere ich den 'Using Index' auf die Abfrage. Ich habe versucht, eine join zurück in die Tabelle, die den "Using index" hält aber dauert etwa 20 Sekunden.

Ich habe versucht, die Spalte eventTime zu einem int anstatt zu einem Datetime zu ändern, aber das schien nicht die Indexnutzung oder Zeit zu beeinflussen.

+0

Was ist Ihre aktuelle Abfrage Timing und was Sie unter verstehen "schnell"? – feeela

+0

Entschuldigung, haben diese Details hinzugefügt –

+0

Haben Sie Indizes für clientId und eventTime? Stellen Sie außerdem sicher, dass Sie den eventTime-Index verwenden und keinen vollständigen Tabellenscan durchführen. –

Antwort

3

Wie Sie wahrscheinlich verstehen, das Problem hier ist der Bereich Zustand ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00' dem (wie es immer der Fall ist) bricht eine effiziente Nutzung von Transactions Index (dh Index eigentlich nur für clientId Gleichung und erste Teil des Bereichs Zustand und die verwendet wird, Index wird nicht zum Gruppieren verwendet).

Am häufigsten ist die Lösung, die die Bereichsbedingung mit einer Gleichheitsprüfung (in Ihrem Fall, führt eine period Spalte Gruppe eventTime in Perioden und ersetzen Sie die BETWEEN Klausel mit einem period IN (1,2,3,4,5)) zu ersetzen. Aber das könnte ein Overhead für Ihren Tisch werden.

Eine andere Lösung, die Sie könnten versuchen, ist eine andere Index hinzufügen (wahrscheinlich Transactions ersetzen, wenn es nicht mehr verwendet wird): (clientId, target1Id, type, eventTime), und verwenden Sie die folgende Abfrage:

SELECT 
    ae.target1Id, 
    ae.type, 
    COUNT(
    NULLIF(ae.eventTime BETWEEN '2011-09-01 03:00:00' 
          AND '2012-09-30 23:57:00', 0) 
) as cnt, 
FROM AuditEvent ae 
WHERE ae.clientId=4 
GROUP BY ae.target1Id, ae.type; 

Auf diese Weise werden Sie a) Umzug der Bereich Zustand bis zum Ende, erlaubt b) den Index für die Gruppierung verwendet wird, c) der Index der abdeckenden Index für die Abfrage machen (das heißt die Abfrage nicht Platte IO-Operationen)

UPD1 benötigen: Es tut mir leid, yesteday Ich habe Ihren Beitrag nicht sorgfältig gelesen und nicht bemerkt, dass Ihr Problem ist, target1Name und target2Name abzurufen. Vor allem bin ich mir nicht sicher, ob Sie die Bedeutung von Using index richtig verstehen. Die Abwesenheit von Using index bedeutet nicht, dass kein Index für die Abfrage verwendet wird. Using index bedeutet, dass der Index selbst genügend Daten enthält, um eine Unterabfrage auszuführen (dh der Index deckt ab). Da target1Name und target2Name in keinem Index enthalten sind, hat die Unterabfrage, die sie abruft, keine Using index.

Wenn Sie die Frage ist nur, wie diese beiden Felder zu Ihrer Anfrage hinzuzufügen (die Sie schnell genug betrachten), dann versuchen Sie doch einmal Folgendes:

SELECT a1.target1Id, a1.type, cnt, target1Name, target2Name 
FROM (
    select ae.target1Id, ae.type, count(*) as cnt, MAX(auditEventId) as max_id 
    from AuditEvent ae 
    where ae.clientId=4 
     and (ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00') 
    group by ae.target1Id, ae.type) as a1 
JOIN AuditEvent a2 ON a1.max_id = a2.auditEventId 
; 
+0

Beide sind tatsächlich gültige Antworten; Ich habe versucht, die Leistung der Abfrage zu erhöhen/Beratung auf verschiedene Möglichkeiten, um es zu strukturieren, und außerdem war ich der beste Weg, um die nicht indizierten Spalten abrufen. Beide Vorschläge haben die Leistung im Vergleich zu den Abfragen, die ich ausprobiert habe, verbessert! –

+0

@nickSpacek, ok, ich bin froh, dass es geholfen hat =) – newtover

Verwandte Themen