2009-05-25 12 views
1

Ich habe eine MySQL 5.1 InnoDB-Tabelle (customers) mit folgenden Struktur:schneller Weg Sätze in MySQL verwendet

int   record_id (PRIMARY KEY) 
int   user_id (ALLOW NULL) 
varchar[11] postcode (ALLOW NULL) 
varchar[30] region (ALLOW NULL) 
.. 
.. 
.. 

Es gibt ungefähr 7 Millionen Zeilen in der Tabelle.

SELECT * FROM customers WHERE user_id IN (32343, 45676, 12345, 98765, 66010, ... 

in der aktuellen Abfrage, die derzeit über 560 user_id s ist in der IN Klausel: Derzeit wird die Tabelle wie folgt abgefragt werden. Bei mehreren Millionen Datensätzen in der Tabelle ist diese Abfrage langsam!

Es gibt sekundäre Indizes auf der Tabelle, von denen die erste auf user_id selbst, was ich dachte, würde helfen.

Ich weiß, dass SELECT(*) A Bad Thing ist und dies wird auf die vollständige Liste der erforderlichen Felder erweitert werden. Die oben nicht aufgeführten Felder sind jedoch int s und double s. Es gibt weitere 50 von denen zurückgegeben werden, aber sie sind für den Bericht benötigt.

Ich kann mir vorstellen, es gibt eine viel bessere Möglichkeit, auf die Daten für die user_id s zuzugreifen, aber ich kann nicht denken, wie es geht. Meine erste Reaktion ist es, die ALLOW NULL auf dem user_id Feld zu entfernen, wie ich NULL Behandlung verstehen, verlangsamt Abfragen?

Ich wäre sehr dankbar, wenn Sie mich in eine effizientere Richtung als die Verwendung der IN () Methode zeigen könnten.

EDIT Ran ERKLÄREN, die sagte:

select_type = SIMPLE 
table = customers 
type = range 
possible_keys = userid_idx 
key = userid_idx 
key_len = 5 
ref = (NULL) 
rows = 637640 
Extra = Using where 

tut diese Hilfe?

+1

Würden Sie bitte eine "erklären" Abfrage ausführen und die Ergebnisse veröffentlichen? – shylent

+0

IN() ist am effizientesten. Das Erstellen einer Spalte NULLable macht im allgemeinen Fall wenig Unterschied (außer wenn Sie einen Ersatzwert anstelle der richtigen NULL verwenden, in diesem Fall ist es vorteilhaft) – MarkR

Antwort

3

Überprüfen Sie zunächst, ob ein Index für USER_IDvorhanden ist, und vergewissern Sie sich, dass verwendet wird.

Sie können es mit running EXPLAIN ausführen.

Zweitens eine temporäre Tabelle erstellen und verwenden in ein JOIN:

CREATE TABLE temptable (user_id INT NOT NULL) 

SELECT * 
FROM temptable t 
JOIN customers c 
ON  c.user_id = t.user_id 

Drittens, wie kann Zeilen ist Ihre Abfrage Rückkehr?

Wenn es fast alle Zeilen zurückgibt, dann wird es nur langsam sein, da es all diese Millionen über den Verbindungskanal pumpen muss, um damit zu beginnen.

NULL wird Ihre Abfrage nicht verlangsamen, da die IN Bedingung erfüllt nur nicht NULL Werte, die indiziert sind.

Update:

Der Index verwendet wird, der Plan ist in Ordnung, außer dass es gibt mehr als eine halbe Million Zeilen.

Müssen Sie wirklich alle diese 638,000 Zeilen in den Bericht einfügen?

Hoffe es ist nicht gedruckt: schlecht für Regenwälder, globale Erwärmung und so.

Im Ernst, Sie scheinen entweder Aggregation oder Seitenumbruch auf Ihrer Abfrage benötigen.

+0

Vielen Dank für die Antwort. Ich werde ein EXPLAIN ausführen und hier zurück posten. Die Abfrage gibt im Moment ~ 638.000 Zeilen zurück. Ich werde versuchen, die user_ids in eine temporäre Tabelle zu setzen, wenn Sie denken, dass das schneller geht. –

+0

EXPLAIN sagt: SELECT_TYPE = SIMPLE table = Kunden type = Bereich possible_keys = userid_idx key = userid_idx key_len = 5 ref = (NULL) rows = 637.640 extra = Mit dem diese Hilfe tut? –

+0

EXPLAIN ist in Ordnung, der Index wird verwendet. Es gibt nur viele oder Reihen, die du nicht zu brauchen scheinst. Aggregieren oder paginieren Sie sie: Kein Mensch kann mehr als 638.000 Zeilen durchsuchen. – Quassnoi

0

Sie können versuchen, die IDs, die Sie abfragen müssen, in eine temporäre Tabelle einzufügen und beide Tabellen zu verbinden. Ich weiß nicht, ob das helfen würde.

1

Sind sie die gleichen ~ 560 IDs jedes Mal? Oder ist es ein anderes ~ 500 IDs auf verschiedenen Abfragen ausgeführt?

Sie könnten einfach Ihre 560 UserIDs in eine separate Tabelle (oder sogar eine temporäre Tabelle) einfügen, einen Index auf die Tabelle setzen und sie mit Ihrer ursprünglichen Tabelle verbinden.

+0

Danke für die Antwort. Sie werden sich jedes Mal ändern. Ich mag die Idee des Temp-Tisches sehr. –

1

Ist dies Ihre wichtigste Abfrage? Ist das eine Transaktionstabelle?

Wenn ja, versuchen Sie, einen Clustered-Index für user_id zu erstellen. Ihre Abfrage ist möglicherweise langsam, da sie weiterhin zufällige Lesevorgänge auf der Festplatte vornehmen muss, um die Spalten abzurufen (Schlüsselsuchen), selbst nachdem die übereinstimmenden Datensätze gefunden wurden (Indexsuche im index user_Id).

Wenn Sie den gruppierten Index nicht ändern können, sollten Sie einen ETL-Prozess in Betracht ziehen (am einfachsten ist ein Trigger, der in eine andere Tabelle mit der besten Indizierung eingefügt wird). Dies sollte schnellere Ergebnisse liefern.

Beachten Sie auch, dass eine solche große Abfragen einige Zeit zu analysieren dauern kann, so dass es helfen, indem die abgefragten ids in eine temporäre Tabelle setzen, wenn possibl

2

„* Wählen Sie“ ist nicht so schlecht, wie manche Leute denken; Bei zeilenbasierten Datenbanken wird die gesamte Zeile abgerufen, wenn sie eine davon abrufen. In Situationen, in denen Sie keinen deckenden Index verwenden, ist "SELECT *" im Grunde nicht langsamer als "SELECT a, b, c" (Hinweis: There ist manchmal eine Ausnahme, wenn Sie große BLOBs haben, aber das ist ein Edge-Case).

Erste Dinge zuerst - Passt Ihre Datenbank in RAM? Wenn nicht, erhalten Sie mehr RAM. Nein im Ernst. Angenommen, Ihre Datenbank ist zu groß, um vernünftig in RAM zu passen (sagen wir,> 32 GB), sollten Sie versuchen, die Anzahl der zufälligen Ein-/Ausgaben zu reduzieren, da sie wahrscheinlich die Dinge aufhalten.

Ich gehe davon aus, dass Sie richtige Server-Hardware mit einem RAID-Controller in RAID1 (oder RAID10 usw.) und mindestens zwei Spindeln laufen. Wenn du nicht bist, geh weg und hol dir das.

Sie könnten definitiv einen Clustered Index verwenden. In MySQL InnoDB können Sie nur den Primärschlüssel clustern, was bedeutet, dass, wenn etwas anderes der Primärschlüssel ist, Sie ihn ändern müssen. Zusammengesetzte Primärschlüssel sind in Ordnung, und wenn Sie viele Abfragen für ein Kriterium (z. B. user_id) durchführen, ist es ein klarer Vorteil, dass es der erste Teil des Primärschlüssels ist (Sie müssen etwas anderes hinzufügen, um es zu erstellen) einzigartig).

Alternativ dazu können Sie Ihre Abfrage möglicherweise mit einem abdeckenden Index versehen. In diesem Fall brauchen Sie die Benutzer-ID nicht als Primärschlüssel (in der Tat darf dies nicht der Fall sein). Dies ist nur möglich, wenn sich alle benötigten Spalten in einem Index befinden, der mit user_id beginnt.

Was die Effizienz der Abfrage betrifft, ist WHERE user_id IN (große Liste von IDs) mit ziemlicher Sicherheit die effizienteste Art, dies aus SQL heraus zu tun.

aber meine größten Tipps sind:

  • Haben Sie ein Ziel vor Augen, herauszufinden, was es ist, und wenn Sie es erreichen, zu stoppen.
  • Sie Wort niemandes nehmen für sie - es versuchen und
  • Stellen Sie sicher, dass Ihre Leistung Testsystem ist die gleiche Hardware-Spezifikation als Produktions
  • Stellen Sie sicher, dass Ihre Leistung Testsystem hat die gleiche Datengröße und Art wie Produktion sehen (Das gleiche Schema ist nicht gut genug!).
  • Verwenden Sie synthetische Daten, wenn Produktionsdaten nicht verwendet werden können (das Kopieren von Produktionsdaten kann logistisch schwierig sein (Denken Sie daran, dass Ihre Datenbank> 32 GB groß ist), da dies ebenfalls gegen Sicherheitsrichtlinien verstoßen kann).
  • Wenn Ihre Abfrage optimal ist (wie es wahrscheinlich bereits ist), versuchen Sie, das Schema und dann die Datenbank selbst zu optimieren.
Verwandte Themen