Die Auswahl einer Abfrage dauert länger als nötig


9

Ich habe eine MySQL-Datenbanktabelle mit fast 23 Millionen Datensätzen. Diese Tabelle hat keinen Primärschlüssel, da nichts eindeutig ist. Es hat 2 Spalten, beide sind indiziert. Unten ist seine Struktur:

Geben Sie hier die Bildbeschreibung ein

Nachfolgend einige Daten:

Geben Sie hier die Bildbeschreibung ein

Jetzt habe ich eine einfache Abfrage ausgeführt:

SELECT `indexVal` FROM `key_word` WHERE `hashed_word`='001'

Leider dauerte es mehr als 5 Sekunden, um die Daten abzurufen und mir zu zeigen. Mein zukünftiger Tisch wird 150 Milliarden Datensätze haben, also ist diese Zeit sehr, sehr hoch.

Ich habe den ExplainBefehl ausgeführt, um zu sehen, was los ist. Ergebnis ist unten.

Geben Sie hier die Bildbeschreibung ein

Dann habe ich das Profil mit dem folgenden Befehl ausgeführt.

SET profiling=1;
SELECT `indexVal` FROM `key_word` WHERE `hashed_word` = '001';
SHOW profile;

Unten sehen Sie das Ergebnis der Profilerstellung:

Geben Sie hier die Bildbeschreibung ein

Nachfolgend finden Sie weitere Informationen zu meinem Tisch:

Geben Sie hier die Bildbeschreibung ein

Warum dauert das so lange? Sie sind auch indiziert! In Zukunft muss ich viele LIKEBefehle ausführen, daher dauert dies zu lange. Was ist schief gelaufen?


"Diese Tabelle hat keinen Primärschlüssel, da nichts eindeutig ist." Ja, richtig ... Zeit, dein Design zu überprüfen. Alle Tabellen sollten einen Primärschlüssel (oder einen eindeutigen Schlüssel) haben.
Ypercubeᵀᴹ

Antworten:


10

Sie fragten " Warum dauert das zu lange ?". Sie sagten auch " Leider dauerte es mehr als 5 Sekunden, um die Daten abzurufen und mir zu zeigen ". Außerdem haben Sie die Profilerstellungsausgabe Ihrer Abfrage gemeldet.

Wie Sie selbst sehen können, zählt die vom Profiler für jeden Schritt gemeldete Summe der Zeiten 0,000154 Sekunden. Aus Sicht des Profilers wurde die Abfrage also in einer solchen Zeit abgeschlossen (0,000154).

Warum erhalten Sie Ergebnisse in " ... mehr als 5 Sekunden? ".

Sie sagten, Sie filtern eine 23-Millionen-Datensatztabelle mit einem 3-Zeichen-Feld. Leider teilen Sie uns nicht mit, wie viele Datensätze Ihre Abfrage zurückgibt ... aber dank der bereitgestellten EXPLAIN SELECT scheint Ihre Abfrage 336052 Datensätze zurückgegeben zu haben.

Es scheint auch, dass alle Ihre Aktivitäten über eine GUI (PHPMyAdmin?) Laufen.

Nach alledem können wir Ihre ursprüngliche Frage wie folgt umformulieren:

"Warum erhalte ich in meiner GUI 336.052 Datensätze, die in mehr als 5 Sekunden angezeigt werden, wenn die MySQL-Ausführungszeit für die zugehörige Abfrage 0,000154 Sekunden beträgt?"

Die Antwort ist meiner Meinung nach recht einfach: 5 Sekunden sind die (wirklich niedrige) Zeit, um 336.052 Datensätze auf dem Weg laufen zu lassen: MySQL-Engine => MySQL-Client-Bibliotheken => PHP MySQL-Modul => Apache => Netzwerk = > Ihr PC TCP / IP-Stack => Browser => DOM-Parser / Builder / etc. => Gerenderte HTML-Seite.

Nach meiner bisherigen Erfahrung ist die für die Übermittlung der Ergebnisse erforderliche Zeit "normalerweise" viel höher als die zum Abrufen solcher Daten erforderliche Zeit. Dies gilt insbesondere dann, wenn Bibliotheken wie PHP-MySQL oder Perl-DBD-MySQL beteiligt sind: Sie benötigen wirklich viel Zeit, um die Datensätze abzurufen, nachdem MySQL alle ordnungsgemäß identifiziert (... und extrahiert) hat.

Wie kann man dieses Problem lösen?

Nochmals ganz einfach: Sind Sie wirklich sicher, dass Sie ALLE 336.052-Datensätze in einem einzigen, vollständigen Datensatz benötigen ?

  • Wenn Ihre Antwort wirklich "JA! Ich brauche alle" lautet, wird Ihre Anwendung die PAGINATION und / oder die BENUTZER-Interaktion selbst erledigen und ... sobald sie alle diese Daten gesammelt hat, wird sie wahrscheinlich viel Zeit aufwenden Interaktion mit dem Benutzer ohne weitere MySQL-Interaktion. In einem solchen Fall sollte es kein Problem sein, 5 Sekunden (oder sogar länger) zu warten.

  • Wenn Ihre Antwort "NEIN, ich möchte mich mit einer" menschlicheren "Datensatzgröße" befassen, müssen Sie Ihre Abfrage (zumindest) verfeinern, damit Sie einen "menschlicheren" Datensatz (Zehner oder "erhalten) höchstens Hunderte). In einem solchen Fall wette ich, dass Sie Ihr Ergebnis in kürzerer Zeit erhalten.


Übrigens: Dies ist genau das gleiche Problem, das Sie in diesem anderen Beitrag bei ServerFault erlebt haben: 88 Sekunden, damit 132 Millionen Datensätze auf dem .... nicht-mysql-streng verwandten magischen Pfad übertragen werden können :-)


Ich erwarte eine Antwort von der op.
Jnanaranjan

5
  1. Überprüfen Sie die MySQL-Datei innodb_buffer_pool_size . Es sollte groß genug sein - je mehr, desto besser. Aber nicht zu viel, um einen Betriebssystemwechsel zu vermeiden.

    show variables like 'innodb_buffer_pool_size'

    zeigt die Puffergröße in Bytes an.

  2. Überprüfen Sie die Abfrage mehrmals. Der erste Lauf kann zu lang sein, da die Daten von der Festplatte in den Speicher gelesen werden sollen. Wenn Sie die Abfrage zum ersten Mal ausführen, befinden sich die Daten immer noch nicht im Innodb-Puffer und müssen von der Festplatte gelesen werden. Das ist viel langsamer, als wenn sich die Daten bereits im Cache befinden würden. Führen Sie die Abfrage daher einige Male aus, um sicherzustellen, dass sie aus dem Cache bereitgestellt wird.

  3. Deaktivieren Sie den Abfrage-Cache, da jeder nachfolgende Lauf von ihm ausgeführt wird und die Testergebnisse verzerrt. In MySQL gibt es einen Mechanismus, der als "Abfrage-Cache" bezeichnet wird und zum Speichern von Abfragen zusammen mit ihren Ergebnissen dient. Wenn MySQL zum zweiten Mal aufgefordert wird, die Abfrage auszuführen, kann es die Ausführung umgehen und die Ergebnisse aus dem Abfragecache abrufen.

  4. Erwägen Sie die Verwendung eines "Deckungsindex":

    ALTER TABLE key_word ADD KEY IX_hashed_word_indexVal (hashed_word, indexVal);

Dies wäre viel effizienter, da MySQL dann die Abfrageanforderung nur aus dem Index erfüllen kann.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.