Vorwort MySQL setzte auch 2016 seinen starken Wachstumstrend bei der Beliebtheit von Datenbanken fort. Immer mehr Kunden erstellen ihre Anwendungen auf MySQL-Datenbanken oder migrieren sogar von Oracle zu MySQL. Einige Kunden stoßen jedoch bei der Verwendung einer MySQL-Datenbank auf Probleme wie etwa langsame Reaktionszeiten und eine Überlastung der CPU. Das kompetente Serviceteam von Alibaba Cloud RDS hat Cloud-Kunden bei der Lösung vieler dringender Probleme geholfen. Einige häufige SQL-Probleme, die im „ApsaraDB Expert Diagnosis Report“ auftreten, sind zu Ihrer Information unten zusammengefasst. 1. LIMIT-Anweisung Die Paginierungsabfrage ist eines der am häufigsten verwendeten Szenarios, aber hier treten normalerweise auch am wahrscheinlichsten Probleme auf. Für die folgende einfache Anweisung besteht die allgemeine DBA-Lösung beispielsweise darin, einen zusammengesetzten Index für die Felder „Typ“, „Name“ und „Erstellungszeit“ hinzuzufügen. Auf diese Weise kann die bedingte Sortierung den Index effektiv nutzen und die Leistung schnell verbessern. WÄHLEN * FROM-Vorgang WO Typ = "SQLStats" UND Name = "SlowLog" ORDER BY Erstellungszeit GRENZE 1000, 10; Nun, wahrscheinlich würden mehr als 90 % der DBAs hier aufhören, um dieses Problem zu lösen. Aber wenn die LIMIT-Klausel zu „LIMIT 1000000,10“ wird, beschweren sich Programmierer immer noch: Warum ist es immer noch langsam, wenn ich nur 10 Datensätze nehme? Sie sollten wissen, dass die Datenbank nicht weiß, wo der 1.000.000ste Datensatz beginnt, und selbst wenn ein Index vorhanden ist, muss dieser von Grund auf neu berechnet werden. Wenn diese Art von Leistungsproblemen auftritt, liegt dies in den meisten Fällen an der Faulheit des Programmierers. In Szenarien wie dem Durchsuchen und Paginieren von Front-End-Daten oder dem Stapelexport großer Datenmengen kann der Maximalwert der vorherigen Seite als Parameter für die Abfragebedingung verwendet werden. Das SQL wird wie folgt neu gestaltet: WÄHLEN * FROM-Operation WO Typ = "SQLStats" UND Name = "SlowLog" UND Erstellungszeit > '2017-03-16 14:00:00' ORDER BY Erstellungszeitlimit 10; Beim neuen Design ist die Abfragezeit grundsätzlich festgelegt und ändert sich nicht, wenn die Datenmenge wächst. 2. Implizite Konvertierung Ein weiterer häufiger Fehler ist die Nichtübereinstimmung zwischen Abfragevariablen und Felddefinitionstypen in SQL-Anweisungen. Beispielsweise die folgende Anweisung: mysql> erweitertes SELECT erklären * > VON mein_guthaben b > WO b.bpn = 14000000123 > UND b.isverified IST NULL; mysql> Warnungen anzeigen; | Warnung | 1739 | Aufgrund einer Typ- oder Sortierungskonvertierung im Feld „bpn“ kann kein Referenzzugriff auf den Index „bpn“ verwendet werden. Das Feld bpn ist als varchar(20) definiert und die Strategie von MySQL besteht darin, die Zeichenfolge vor dem Vergleich in eine Zahl umzuwandeln. Die Funktion wirkt auf das Tabellenfeld und der Index wird ungültig. In der oben beschriebenen Situation handelt es sich möglicherweise um Parameter, die automatisch vom Anwendungsframework ausgefüllt werden und nicht um die ursprüngliche Absicht des Programmierers. Heutzutage gibt es viele komplexe Anwendungsframeworks. Sie sind zwar bequem zu verwenden, Sie sollten sich jedoch vor den Fallstricken in Acht nehmen, die sie mit sich bringen können. 3. Verknüpfungen aktualisieren und löschen Obwohl MySQL 5.6 die Materialisierungsfunktion eingeführt hat, ist es wichtig zu beachten, dass sie derzeit nur Abfrageanweisungen optimiert. Aktualisierungen oder Löschungen müssen manuell in JOINs umgeschrieben werden. Beispielsweise führt MySQL in der folgenden UPDATE-Anweisung tatsächlich eine Schleife/verschachtelte Unterabfrage (DEPENDENT SUBQUERY) aus, und man kann sich die Ausführungszeit vorstellen. UPDATE-Vorgang o SET-Status = "Bewerben" WO o.id IN (SELECT id VON (AUSWÄHLEN o.id, o.Status FROM-Operation o WO o.Gruppe = 123 UND o.status NICHT IN ( 'erledigt' ) ORDER BY o.übergeordnetes Element, o.id GRENZE 1) t); Ausführungsplan: +----+--------------------+-------+-------+---------------+---------+---------+---------+-----------+---------+----------------------------------------------------+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra | +----+--------------------+-------+-------+---------------+---------+---------+---------+-----------+---------+----------------------------------------------------+ | 1 | PRIMARY | o | Index | | PRIMARY | 8 | | 24 | Verwenden von „where“; Verwenden von „temporär“ | | 2 | ABHÄNGIGE UNTERABFRAGE | | | | | | | | | | Unmögliches WHERE nach dem Lesen von Konstantentabellen bemerkt | | 3 | ABGELEITET | o | ref | idx_2,idx_5 | idx_5 | 8 | const | 1 | Where verwenden; Filesort verwenden | +----+--------------------+-------+-------+---------------+---------+---------+---------+-----------+---------+----------------------------------------------------+ Nach dem Umschreiben als JOIN ändert sich der Unterabfrageauswahlmodus von DEPENDENT SUBQUERY zu DERIVED und die Ausführungsgeschwindigkeit wird erheblich von 7 Sekunden auf 2 Millisekunden beschleunigt. UPDATE-Vorgang o JOIN (AUSWÄHLEN o.id, o.Status FROM-Operation o WO o.Gruppe = 123 UND o.status NICHT IN ( 'erledigt' ) ORDER BY o.übergeordnetes Element, o.id GRENZE 1) t EIN o.id = t.id SET-Status = „Bewerben“ Der Ausführungsplan wird vereinfacht zu +----+-----------+------+-----------+---------------+-----------+---------+-----------+---------+-------+----------------------------------------------------+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra | +----+-----------+------+-----------+---------------+-----------+---------+-----------+---------+-------+----------------------------------------------------+ | 1 | PRIMARY | | | | | | | | | | Unmögliches WHERE nach dem Lesen von Const-Tabellen bemerkt | | 2 | ABGELEITET | o | ref | idx_2,idx_5 | idx_5 | 8 | const | 1 | Where verwenden; Filesort verwenden | +----+-----------+------+-----------+---------------+-----------+---------+-----------+---------+-------+----------------------------------------------------+ 4. Gemischte Sortierung MySQL kann keine Indizes für gemischte Sortierungen verwenden. In manchen Szenarien gibt es jedoch immer noch Möglichkeiten, durch den Einsatz spezieller Methoden die Leistung zu verbessern. WÄHLEN * VON meiner_Bestellung o INNER JOIN my_appraise a ON a.orderid = o.id ORDER BY a.is_reply ASC, a.appraise_time DESC GRENZE 0, 20 Der Ausführungsplan zeigt einen vollständigen Tabellenscan: +----+----------+----------+--------+----------+---------+---------+-----------------------+---------+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra +----+----------+----------+--------+----------+---------+---------+-----------------------+---------+ | 1 | SIMPLE | a | ALL | idx_orderid | NULL | NULL | NULL | 1967647 | Filesort verwenden | | 1 | EINFACH | o | eq_ref | PRIMARY | PRIMARY | 122 | a.orderid | 1 | NULL | +----+----------+---------+---------+---------+---------+---------+----------------+---------+-+ Da is_reply nur zwei Zustände hat, 0 und 1, schreiben wir es wie folgt um und die Ausführungszeit wird von 1,58 Sekunden auf 2 Millisekunden reduziert. WÄHLEN * VON ((AUSWÄHLEN * VON meiner_Bestellung o INNER JOIN my_appraise a EIN a.orderid = o.id UND ist_Antwort = 0 ORDER BY Bewertungszeit DESC GRENZE 0, 20) UNION ALLE (WÄHLEN * VON meiner_Bestellung o INNER JOIN my_appraise a EIN a.orderid = o.id UND ist_Antwort = 1 ORDER BY Bewertungszeit DESC GRENZE 0, 20)) t ORDER BY ist_Antwort ASC, Bewertungszeit DESC GRENZE 20; 5. EXISTS-Anweisung Wenn MySQL die EXISTS-Klausel behandelt, verwendet es weiterhin die Methode zur Ausführung verschachtelter Unterabfragen. Wie zum Beispiel die folgende SQL-Anweisung: WÄHLEN * VON meinem_Nachbarn n LINKS BEITRETEN my_neighbor_apply sra EIN n.id = sra.neighbor_id UND sra.user_id = 'xxx' WO n.Themenstatus < 4 UND EXISTIERT (WÄHLEN SIE 1 FROM message_info m WO n.id = m.nachbar_id UND m.inuser = 'xxx') UND n.Thementyp <> 5 Der Ausführungsplan lautet: +----+--------------------+-------+------+-----+------------------------------------------+---------+-----------+---------+ -----+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra | +----+--------------------+-------+------+ -----+------------------------------------------+---------+-----------+---------+ -----+ | 1 | PRIMARY | n | ALL | | NULL | NULL | NULL | 1086041 | Verwenden von „where“ | | 1 | PRIMARY | sra | ref | | idx_user_id | 123 | const | 1 | Verwenden von where | | 2 | ABHÄNGIGE UNTERABFRAGE | m | ref | | idx_message_info | 122 | const | 1 | Indexbedingung verwenden; Where verwenden | +----+--------------------+-------+------+ -----+------------------------------------------+---------+-----------+---------+ -----+ Durch das Entfernen von „Exists“ und Ändern in „Join“ können verschachtelte Unterabfragen vermieden und die Ausführungszeit von 1,93 Sekunden auf 1 Millisekunde reduziert werden. WÄHLEN * VON meinem_Nachbarn n INNER JOIN message_info m EIN n.id = m.Nachbar-ID UND m.inuser = 'xxx' LINKS BEITRETEN my_neighbor_apply sra EIN n.id = sra.neighbor_id UND sra.user_id = 'xxx' WO n.Themenstatus < 4 UND n.Thementyp <> 5 Neuer Ausführungsplan: +----+----------+----------+--------+ -----+---------------------------------------+---------+ -----+------+ -----+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra | +----+----------+----------+--------+ -----+---------------------------------------+---------+ -----+------+ -----+ | 1 | SIMPLE | m | ref | | idx_message_info | 122 | const | 1 | Indexbedingung verwenden | | 1 | SIMPLE | n | eq_ref | | PRIMARY | 122 | ighbor_id | 1 | Verwenden von „where“ | | 1 | SIMPLE | sra | ref | | idx_user_id | 123 | const | 1 | Verwenden von where | +----+----------+----------+--------+ -----+---------------------------------------+---------+ -----+------+ -----+ 6. Bedingter Pushdown Zu den Fällen, in denen die äußeren Abfragebedingungen nicht auf komplexe Ansichten oder Unterabfragen übertragen werden können, gehören:
Wie in der folgenden Anweisung gezeigt, zeigt der Ausführungsplan, dass die Bedingung nach der Aggregat-Unterabfrage wirkt: WÄHLEN * FROM (SELECT Ziel, Zählen(*) FROM-Operation GROUP BY Ziel) t WO Ziel = "rm-xxxx" +----+----------+------------+-------+---------------+---------------+-------------+---------+-----------+---------+----------+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra | +----+----------+------------+-------+---------------+---------------+-------------+---------+-----------+---------+----------+ | 1 | GRUNDSCHULE | <abgeleitet2> | Referenz | <auto_key 0 > | <auto_key0> | 514 | konstant | 2 | Verwenden von „where“ | | 2 | ABGELEITET | Operation | Index | idx_4 | idx_4 | 519 | NULL | 20 | Index wird verwendet | +----+----------+------------+-------+---------------+---------------+-------------+---------+-----------+---------+----------+ Nachdem Sie festgestellt haben, dass die Abfragebedingungen aus semantischer Sicht direkt nach unten verschoben werden können, schreiben Sie sie wie folgt um: Ziel auswählen, Zählen(*) FROM-Operation WO Ziel = "rm-xxxx" GROUP BY-Ziel Der Ausführungsplan wird: +----+----------+----------+------+---------------+-----------+-----------+---------+-----------+---------+--------------------+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra | +----+--------------+----------+------+---------------+-----------+-----------+---------+-----------+---------+--------------------+ | 1 | SIMPLE | Operation | ref | idx_4 | idx_4 | 514 | const | 1 | Mit where; Mit index | +----+----------+----------+------+---------------+-----------+-----------+---------+-----------+---------+--------------------+ Eine ausführliche Erklärung, warum die externen Bedingungen von MySQL nicht nach unten verschoben werden können, finden Sie im Artikel: http://mysql.taobao.org/monthly/2016/07/08 7. Den Umfang im Vorfeld eingrenzen Zunächst die anfängliche SQL-Anweisung: WÄHLEN * VON meiner_Bestellung o LINKS BEITRETEN my_userinfo u EIN o.uid = u.uid LINKS BEITRETEN my_productinfo p EIN o.pid = p.pid WO ( o.display = 0 ) UND ( o.ostaus = 1 ) ORDER BY o.Verkaufszeit DESC GRENZE 0, 15 Die ursprüngliche Absicht dieser SQL-Anweisung ist: Führen Sie zuerst eine Reihe von Linksverknüpfungen durch und sortieren Sie dann die ersten 15 Datensätze und nehmen Sie sie. Aus dem Ausführungsplan ist auch ersichtlich, dass die geschätzte Anzahl der sortierten Datensätze im letzten Schritt 900.000 beträgt und der Zeitaufwand 12 Sekunden beträgt. +----+----------+----------+--------+------------+---------+---------+---------+----------------+--------+----------------------------------------------------+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra | +----+----------+----------+--------+------------+---------+---------+---------+----------------+--------+----------------------------------------------------+ | 1 | SIMPLE | o | ALL | NULL | NULL | NULL | NULL | 909119 | Where verwenden; Temporär verwenden; Filesort verwenden | | 1 | EINFACH | u | eq_ref | PRIMARY | PRIMARY | 4 | o.uid | 1 | NULL | | 1 | SIMPLE | p | ALL | PRIMARY | NULL | NULL | NULL | 6 | Verwenden von where; Verwenden von Join-Puffern (Block Nested Loop) | +----+----------+----------+--------+------------+---------+---------+---------+----------------+--------+----------------------------------------------------+ Da sich sowohl die abschließende WHERE-Bedingung als auch die Sortierung auf die äußerste linke Primärtabelle beziehen, können Sie my_order im Voraus sortieren, um die Datenmenge zu reduzieren, bevor Sie einen Left Join ausführen. Nach dem Neuschreiben von SQL reduziert sich die Ausführungszeit auf etwa 1 Millisekunde. WÄHLEN * AUS ( WÄHLEN * VON meiner_Bestellung o WO ( o.display = 0 ) UND ( o.ostaus = 1 ) ORDER BY o.Verkaufszeit DESC GRENZE 0, 15 ) LINKS BEITRETEN my_userinfo u EIN o.uid = u.uid LINKS BEITRETEN my_productinfo p EIN o.pid = p.pid ORDER BY o.Verkaufszeit DESC Grenze 0, 15 Überprüfen Sie den Ausführungsplan erneut: Die Unterabfrage nimmt nach der Materialisierung an JOIN teil (select_type=DERIVED). Obwohl die geschätzte Anzahl der zu scannenden Zeilen immer noch 900.000 beträgt, wird die tatsächliche Ausführungszeit nach Verwendung des Index und der LIMIT-Klausel sehr gering. +----+----------+------------+--------+------------+---------+---------+---------+---------+---+--------+----------------------------------------------------+ | ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra | +----+----------+------------+--------+------------+---------+---------+---------+---------+---+--------+----------------------------------------------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 15 | Temporär verwenden; Filesort verwenden | | 1 | PRIMARY | u | eq_ref | PRIMARY | PRIMARY | 4 | o.uid | 1 | NULL | | 1 | PRIMARY | p | ALL | PRIMARY | NULL | NULL | NULL | 6 | Verwenden von where; Verwenden von Join-Puffern (Block Nested Loop) | | 2 | ABGELEITET | o | index | NULL | idx_1 | 5 | NULL | 909112 | Verwenden von „where“ | +----+----------+------------+--------+------------+---------+---------+---------+---------+---+--------+----------------------------------------------------+ 8. Zwischenergebnismengen nach unten verschieben Schauen wir uns das folgende Beispiel an, das zunächst optimiert wurde (die Haupttabelle im linken Join hat Vorrang vor der Abfragebedingung): WÄHLEN Sie a.*, c.zugewiesen AUS ( SELECT Ressourcen-ID VON my_distributed Wobei isdelete = 0 UND cusmanagercode = '1234567' ORDER BY Salecode Limit 20) a LINKS VERBINDEN ( SELECT resourcesid, sum(ifnull(allocation, 0) * 12345) zugewiesen VON meinen_Ressourcen GROUP BY Ressourcen-ID) c EIN a.resourceid = c.resourcesid Gibt es noch weitere Probleme mit dieser Aussage? Es ist nicht schwer zu erkennen, dass es sich bei Unterabfrage c um eine Volltabellenaggregationsabfrage handelt. Wenn die Anzahl der Tabellen besonders groß ist, verschlechtert sich die Leistung der gesamten Anweisung. Tatsächlich sind für die Unterabfrage c im endgültigen Ergebnissatz des linken Joins nur die Daten relevant, die mit der Ressourcen-ID der Haupttabelle übereinstimmen können. Daher können wir die Anweisung wie folgt umschreiben und die Ausführungszeit wird von den ursprünglichen 2 Sekunden auf 2 Millisekunden reduziert. WÄHLEN Sie a.*, c.zugewiesen AUS ( SELECT Ressourcen-ID VON my_distributed WO isdelete = 0 UND cusmanagercode = '1234567' ORDER BY Salecode Limit 20) a LINKS VERBINDEN ( SELECT resourcesid, sum(ifnull(allocation, 0) * 12345) zugewiesen VON meinen_Ressourcen r, ( SELECT Ressourcen-ID VON my_distributed WO isdelete = 0 UND cusmanagercode = '1234567' ORDER BY Salecode Limit 20) a WO r.resourcesid = a.resourcesid GROUP BY Ressourcen-ID) c EIN a.resourceid = c.resourcesid Aber Unterabfrage a kommt in unserer SQL-Anweisung mehrmals vor. Diese Schreibweise verursacht nicht nur zusätzlichen Aufwand, sondern lässt die gesamte Aussage auch kompliziert erscheinen. Schreiben Sie es noch einmal mit der WITH-Anweisung: MIT EINEM ( SELECT Ressourcen-ID VON my_distributed Wobei isdelete = 0 UND cusmanagercode = '1234567' ORDER BY Verkaufscode-Limit 20) WÄHLEN Sie a.*, c.zugewiesen VON einem LINKS VERBINDEN ( SELECT resourcesid, sum(ifnull(allocation, 0) * 12345) zugewiesen VON meinen_Ressourcen r, A WO r.resourcesid = a.resourcesid GROUP BY Ressourcen-ID) c EIN a.resourceid = c.resourcesid Zusammenfassen Der Datenbankcompiler generiert einen Ausführungsplan, der bestimmt, wie das SQL tatsächlich ausgeführt wird. Aber der Compiler gibt nur sein Bestes und nicht alle Datenbankcompiler sind perfekt. Bei den meisten der oben genannten Szenarien treten auch Leistungsprobleme bei anderen Datenbanken auf. Nur wenn Sie die Eigenschaften des Datenbankcompilers verstehen, können Sie dessen Mängel vermeiden und leistungsstarke SQL-Anweisungen schreiben. Wenn Programmierer Datenmodelle entwerfen und SQL-Anweisungen schreiben, sollten sie die Idee von Algorithmen einbringen oder sich mit ihnen auskennen. Beim Schreiben komplexer SQL-Anweisungen sollten Sie sich die Verwendung von WITH-Anweisungen angewöhnen. Durch prägnante und übersichtliche SQL-Anweisungen kann zudem die Datenbank entlastet werden. Das ist alles für diesen Artikel. Ich hoffe, dass der Inhalt dieses Artikels für Ihr Studium oder Ihre Arbeit von gewissem Referenzwert ist. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM. Das könnte Sie auch interessieren:
|
<<: So kapseln Sie die Rich-Text-Komponente von WangEditor in Angular
>>: Implementierung der Clusterkonstruktion im Docker Redis5.0-Cluster
Die Installationsmethode von MySQL5.7 rpm unter L...
Die -9999-Pixel-Bildersetzungstechnologie ist seit...
Inhaltsverzeichnis Auswirkungen eines vollständig...
1. Lösung für das Problem, dass die Seite leer is...
SQL ist der Hauptstamm. Warum ich das so verstehe...
Manchmal muss MySQL eine Funktion ähnlich zu last...
Inhaltsverzeichnis Geben Sie das Thema MySQL ein:...
Da Uniapp nicht über eine autorisierte DingTalk-A...
Die Generierung und Überprüfung von Zufallscodes ...
In diesem Artikel wird der spezifische JavaScript...
Inhaltsverzeichnis 1. Definition und Aufruf des K...
1. Grund Ich musste MySQL nur auf einem neuen Sys...
Die Farbabstimmung beim Erstellen einer Website i...
Hintergrund Kürzlich fragten mich einige Freunde,...
Inhaltsverzeichnis 1. Ändern Sie die Datei my.cnf...