2009-07-30 20 views
2

Ich verwende die folgende MySQL-Abfrage in einem PHP-Skript in einer Datenbank, die über 370.000.000 (ja, dreihundertsiebzig Millionen) Zeilen enthält. Ich weiß, dass es sehr ressourcenintensiv ist und es ewig dauert, diese eine Abfrage auszuführen. Weiß jemand, wie ich entweder die Abfrage optimieren kann oder die Informationen auf andere Weise schneller bekommen kann?Wie kann ich diese MySQL-Abfrage optimieren?

Tabelle Info:

games | longint, unsigned, Primary Key 
win | bit(1) 
loss | bit(1)

Abfrage:

SELECT MID(game,{$len},1) AS move, 
     COUNT(*) AS games, 
     SUM(win) AS wins, 
     SUM(loss) AS losses 
FROM games 
WHERE game>{$something} AND game<{$something_else} 
GROUP BY move

Vielen Dank im Voraus für Ihre Hilfe!

+2

Es ist sehr falsch, LIKE für einen numerischen Wert zu verwenden.Gleich für diese Gruppierung mit MID(), es klingt falsch. Warum machst du das? Schließlich, anstatt mehrere Spalten für "gewinnen" und "Verlust" warum nicht eine Spalte "Ergebnis", deren Wert könnte "gewinnen", "Verlust" oder "zeichnen". –

+0

Es ist entweder ein LIKE oder größer als und kleiner als. Ich hätte nicht gedacht, dass es so oder so viel bewirken würde. Die Gruppierung nach MID() gruppiert grundsätzlich die nächste Ziffer in "Spiel". Wie MID (Spiel, 1, {$ len}). Die Gewinn/Verlust-Sache nimmt auf beiden Seiten 2 Bits Platz in Anspruch, also spielt es keine Rolle. – dampkwab

+0

Bei der Verwendung von LIKE für numerische Werte ist Typ-Casting erforderlich, daher muss es weniger effizient sein. Was die Gewinn/Verlust-Spalten betrifft, fürchte ich, dass sie jeweils 1 Byte belegen, nicht nur 1 Bit. Für die Frage "warum" hätte ich vielleicht spezifischer sein sollen. Die wirkliche Frage ist, warum Sie nach Bereich suchen müssen, LIKE verwenden und das Ergebnis dann gruppieren, indem Sie die Textdarstellung einer Zahl verwenden. Ich weiß nicht, welche Daten Ihre "Spiele" Spalte tatsächlich hält (das ist wirklich die Art von Dingen, die Sie beschreiben sollten BTW), aber es scheint, dass Ihr Problem ist Ihre Datenbank-Design, nicht die ineffizienten Abfragen, die Sie haben. –

Antwort

5

Der einzige Vorschlag, den ich machen kann, ist eine Tabelle zu verwenden, um vorauszuberechnen alle zählt und Summen für jedes Spiel und aktualisieren, wenn Tabelle Spiel Änderungen einen Trigger verwenden.

+0

Das würde Hunderte von Millionen mehr Zeilen erfordern, als derzeit verwendet werden, und auch mehr Platz pro Zeile. Der Tisch ist schon fast 10GiB, also halte ich das nicht für praktikabel. – dampkwab

+0

Aber Ihr Hauptproblem ist, dass die Berechnungen zu teuer sind. Das Verwenden von etwas mehr Speicherplatz zum Erstellen einer "Cache" -Tabelle kann hilfreich sein. – slipbull

+0

OK, ich denke, ich werde ein Cachesystem aufbauen. Ich bin immer noch besorgt über den Platzbedarf. – dampkwab

0

Wenn Sie stark belesen werden, sollten Sie eine aggregierte Tabelle über die Daten, die Sie normalerweise abfragen, behalten und pflegen.

+0

Leider verwende ich alle möglichen Werte für '$ game' mit ungefähr gleicher Häufigkeit. – dampkwab

+0

Das bedeutet nur, dass Sie nach den verschiedenen Werten von $ Spielen aggregieren, die Sie benötigen. Selbst wenn das in Millionenhöhe geht, wird es wahrscheinlich viel weniger als die einzelnen Datensätze sein. – nos

0

Hört sich an, als könntest du das denormalisieren und eine "Moves" -Tabelle erstellen, die Statistiken per "move" aufzeichnet, nicht nur per "Spiel".

0

Sie können "Geschwindigkeit kaufen", indem Sie Speicherplatz opfern oder Speicherplatz reservieren, aber eine schlechtere Leistung erzielen. Da Ihr Problem Geschwindigkeit ist, benötigen Sie einige Vorberechnungen. Und ja, einige Profilerstellung der Abfrage.

BTW, die "Großen" hatten früher unterschiedliche Konfigurationen (unterschiedliche Hardware und Einstellungen) für OLTP (für die eigentlichen Transaktionen in Echtzeit) und DW (Analyse großer Datenmengen).

0

Die Funktion mid() tötet diese Abfrage. MySQL muss eine temporäre Tabelle im Speicher erstellen, um mit der Funktion mid() umgehen zu können, und wegen der Gruppe nach.

Ich gehe davon aus, dass $ Spiel ist die Art von Spiel. (Kontrolleure, Schach, Tic Tac Toe)

Ich würde einen anderen Tisch für die Art des Spiels aushängen. Dies ermöglicht Ihrer Gruppe, einen Index zu verwenden, der viel schneller wäre.

Ich schlage vor, so etwas wie:

[game] 
game bigint unsigned 
win bit 
loss bit 
game_type_id bigint unsigned 

[game_type] 
game_type_id bigint unsigned 
game_type_desc varchar(13) 

mit Abspaltungen Aussagen auf einem Tisch dieses großen Vorsicht. Erstellen Sie immer eine Sicherungskopie, bevor Sie eine Änderung vornehmen.

+0

Eigentlich ist '$ game' eine 15-stellige Quinary-Nummer. : P Auch, wie es in der Frage sagt, muss MID() in der Lage sein, 5 verschiedene Längenwerte zu akzeptieren, so dass es eine RIESIGE Zusatztabelle wäre! – dampkwab

+0

Es erstellt einen Teil der großen zusätzlichen Tabelle im Speicher jedes Mal, wenn die Abfrage ausgeführt wird und Ihnen nicht den Vorteil eines Indexes bietet. – txyoji

1

Ich würde sofort aufhören, die Abfrage MID() sowohl in der SELECT-Ausdruck und GROUP BY verwenden. Depening auf Abfragebedingungen, MySQL wird in einem einzigen Ausdruck nicht notwendigerweise, dass zwischenzuspeichern, während das Parsen, so zumindest versuchen, diese:

SELECT MID(game,{$len},1) AS move, 
    COUNT(*) AS games, 
    SUM(win) AS wins, 
    SUM(loss) AS losses 
    FROM games WHERE game LIKE '{$game}%' GROUP BY move; 

Nicht in der Welt die größte Veränderung, aber es sollte einen kleinen Unterschied machen. Abgesehen davon behaupte ich, dass die einzige wirkliche Möglichkeit zur Optimierung dieser Art der Speicherung der Daten darin besteht, diese Werte vorab zu berechnen und sie zu erhöhen, wenn ein Spiel beendet ist.

+1

Oh, vielen Dank. Ich wusste nicht, dass ich "GROUP BY" eine Kolumne, die ich gerade erfunden habe. Außerdem sollte ich beachten, dass ich die 'LIKE' in eine'> x AND dampkwab

+0

Sie Abfrage sollte viel schneller mit der GROUP BY-Änderung und> und und