Wie kann ich das Anzeigen von Spalten in MySQL beschleunigen?

7

Meine Anwendung hängt davon ab, dass für bestimmte Tabellen "Spalten anzeigen" ausgeführt wird. Die Ausführung dauert ungefähr 60 ms, während alle anderen Abfragen weniger als eine ms dauern. Das information_schemadirekte Abfragen ist noch langsamer.

Die Datenbank enthält ungefähr 250 Datenbanken mit 100 bis 200 Tabellen pro Datenbank (insgesamt ungefähr 20.000 Tabellen).

  • Wie kann ich herausfinden, warum diese Vorgänge so langsam sind?
  • Gibt es vielleicht eine Einstellung, die ich ändern kann, um sie schneller laufen zu lassen oder um sie SQL-seitig zwischenzuspeichern?

(Die Anwendung führt ungefähr 14 solcher Abfragen pro Seitenladevorgang durch. Ich bin mir bewusst, dass dieser Legacy-Code bereinigt werden muss, suche aber nach möglichen Optionen, während ich an der langfristigen Korrektur arbeite.)

mpen
quelle
1
In welchem ​​Szenario wären 60 ms aus Interesse zu langsam, um die Spalten einer Tabelle zu untersuchen? Es ist nicht etwas, das Sie bei jeder Anfrage tun sollten
1
Was meinst du mit Spalten anzeigen? Spaltennamen aus einer Tabelle entfernen oder eine ganze Spalte drucken? Wenn es die Namen sind ... warum greifst du es nicht einfach einmal und speicherst es in deiner Anwendung ... wenn das nicht möglich ist, warum erstellst du nicht einfach eine andere Tabelle, die alle auf der Tabelle basierenden Spalten enthält?
@Jaitsu: Nein, das sollten wir nicht tun, aber so ist es nun mal . Legacy-Code. Bis ich etwas Zeit habe, um es aufzuräumen und richtig zu machen, möchte ich sehen, ob ich es beschleunigen kann. Ich habe ungefähr 14 davon, die jede Seite laden.
@FlorinStingaciu: Ja, Spaltennamen. Wenn Sie sie in eine andere Tabelle einfügen, wird dies möglicherweise beschleunigt, aber es wird nicht mehr synchronisiert, was den gesamten Zweck, die Tabelle direkt zu fragen, zunichte macht.
1
@ Mat: Keine schlechte Idee. Gewählt für die Migration zu dba.

Antworten:

12

MySQL berechnet Tabellenstatistiken für bestimmte Vorgänge neu, die auf INFORMATION_SCHEMATabellen zugreifen (dies SHOW COLUMNSist nur ein praktischer Alias ​​für die Abfrage INFORMATION_SCHEMA.COLUMNS). Setzen Sie innodb_stats_on_metadata auf false, um zu verhindern, dass diese Neuberechnung erfolgt, wenn Sie Metadaten aus der Tabelle anfordern .

SET GLOBAL innodb_stats_on_metadata=0;

und fügen Sie Folgendes hinzu my.cnf

[mysqld]
innodb_stats_on_metadata = 0
Aaron Brown
quelle
Ich hätte erwähnen sollen, dass ich tatsächlich MyISAM verwende. Ich habe trotzdem versucht, dies einzustellen, aber es hat keinen Nutzen gebracht.
Mpen
Haben Sie ALTER TABLE foo ENGINE = InnoDB in Betracht gezogen? :) Gibt es einen guten Grund für die Verwendung von MyISAM?
Aaron Brown
Meistens Legacy-Gründe, denke ich. Ich fürchte, was könnte passieren, wenn ich das versuchen würde? Ich bin mir nicht sicher, ob alle FKs in einer Reihe stehen. Ich werde noch etwas darüber nachdenken.
Mpen
@AaronBrown +1 für diese Antwort, denn wer mit einer All-InnoDB-Datenbank in diese Situation gerät, benötigt diese Informationen.
RolandoMySQLDBA
1
+1 für das Setzen [mysqld]dort. Für viele mag es offensichtlich sein, dass diese Einstellung unter mysqld fällt, aber für diejenigen, die diese Frage stellen würden, ist dies möglicherweise nicht offensichtlich. Übrigens beschleunigte sich dies SELECT COUNT(*)auf einem meiner information_schemaTische von über einer Minute auf 6 Sekunden. Immer noch langsam, aber eine enorme Verbesserung.
Buttle Butkus
3

Ich schlage vor, Sie erstellen eine Datenbank, die die INFORMATION_SCHEMATabellen (oder nur die, die Sie benötigen) als Replikate enthält. Indizieren Sie sie entsprechend und Sie erhalten Leistungsgewinn.

Das Problem der Synchronisierung zwischen dieser Datenbank und INFORMATION_SCHEMAist jedoch schwierig.

Sie könnten eine Prozedur haben, die diese Tabellen jede Stunde oder alle 5 Minuten synchronisiert (wie oft wird die Tabellenstruktur geändert?).

Eine andere Idee wäre, MySQL Proxy zu verwenden, um alle ALTER TABLEAnweisungen (und CREATEund DROPund CREATE INDEXalle anderen Anweisungen, die die benötigten Informationen ändern) abzufangen und das replizierte Informationsschema zu synchronisieren, nachdem diese Anweisungen erfolgreich waren.


Wenn Sie nur die Spaltennamen und keine anderen Informationen wie Datentyp, Länge oder verfügbare Indizes benötigen, können Sie die Verwendung von möglicherweise durch SHOW COLUMNS(schnelle) Abfragen ersetzen, die nur 1 Zeile zurückgeben, mit LIMIT 1oder gar keiner, mit entweder LIMIT 0oder:

SELECT * FROM TableName WHERE FALSE ;

Trotz der allgemeinen Ratschläge gegen die Verwendung von SELECT *kann dies ein legitimer Fall sein, in dem nichts anderes nützlich ist. (Alles andere *kann zu Fehlern führen!)

ypercubeᵀᴹ
quelle
2

In diesem speziellen Fall denke ich, dass INFORMATION_SCHEMAes sich um einen roten Hering handelt. Nach meinen eigenen SHOW COLUMNSLeistungstests innodb_stats_on_metadatascheint die Variable weder für MyISAM- noch für InnoDB-Tabellen einen Unterschied zu machen.

Aus dem MySQL 5.0 Handbuch ...

Einige Bedingungen verhindern die Verwendung einer temporären In-Memory-Tabelle. In diesem Fall verwendet der Server stattdessen eine On-Disk-Tabelle:

[...]

  • Die Anweisungen SHOW COLUMNSund The DESCRIBEwerden BLOBals Typ für einige Spalten verwendet. Daher ist die für die Ergebnisse verwendete temporäre Tabelle eine Tabelle auf der Festplatte.

Dies scheint ab MySQL 5.5 aus dem Handbuch entfernt worden zu sein, scheint aber in dieser Version immer noch zu gelten ...

mysql> SHOW VARIABLES LIKE 'version';
+---------------+-------------------------+
| Variable_name | Value                   |
+---------------+-------------------------+
| version       | 5.5.41-0ubuntu0.14.04.1 |
+---------------+-------------------------+
1 row in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0     |
+-------------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW COLUMNS FROM mysql.user;
[...snip...]
42 rows in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 1     |
+-------------------------+-------+
1 row in set (0.00 sec)

Die Feldinformationen mit einem Abfrageergebnis zurückgegeben enthalten die gleichen Informationen wie zurück durch SHOW COLUMNS, so dass eine SELECT * FROM my_table LIMIT 0dasselbe zu erreichen , soll eine On-Disk temporäre Tabelle ohne die Erstellung pro Abfrage.

Ein kurzes Beispiel, um einfach die Feldnamen in PHP zu erfassen ...

$mysql = new mysqli('localhost', 'root', '', 'my_database');
$field_names = array();
$result = $mysql->query("SELECT * FROM my_table LIMIT 0");
$fields = $result->fetch_fields();
foreach ($fields as $fields)
{
    $field_names[] = $field->name;
}
var_dump($field_names);

Das Abrufen von Feldinformationen auf diese Weise ist etwas umständlicher zu dekodieren. Sie müssen die Beschreibung der zugrunde liegenden MYSQL_FIELDStruktur konsultieren , um die Datentypen und Flags abzurufen, aber sie läuft auf meinem System etwa siebenmal schneller.

Aya
quelle
1

Ich mag den ersten Vorschlag in der Antwort von @ yerpcube (+1), aber ich möchte etwas vorschlagen

  • Erstellen Sie eine weitere Datenbankinstanz an Port 3307
  • mysqldump die Produktionsdatenbank in SQL Text File mit den folgenden Optionen:
    • --no-data
    • --routines
    • --triggers
    • --all-databasesoder --databasesgefolgt von einer Liste der gewünschten Datenbanken
  • Laden Sie die SQL-Textdatei in die MySQL-Instanz von Port 3307

Daher sollte mysqldump wie folgt aussehen:

mysqldump --no-data --routines --triggers --all-databases > ImportFile.sql

Das ist es. In Zukunft müssen Sie lediglich eine Verbindung zu dieser Datenbankinstanz von Port 3307 herstellen und alle schemabezogenen Abfragen nach Herzenslust ausführen. Sollten Sie eine Tabelle in der Produktionsdatenbank kennen, die sich ändert, mysqldump das Schema aus der Produktion und laden Sie es erneut in die Port 3307-Instanz.

WARNUNG: Wenn Sie eine MySQL-Instanz auf demselben Computer wie die Produktion installieren, stellen Sie unbedingt sicher, dass Sie eine Verbindung zu dieser Instanz herstellen

mysql -u... -p... -h127.0.0.1 -P3307 < ImportFile.sql

Wenn Sie ausführen

mysql -u... -p... -P3307 < ImportFile.sql

Es wird die Schlauchproduktion. Also sei vorsichtig !!!!

Eine Alternative wäre, nur einen separaten DB-Server zu verwenden.

RolandoMySQLDBA
quelle