Die Auslastung der MySQL-Datenbank wird durch die Anzahl der verarbeiteten Abfragen bestimmt. Es gibt mehrere Situationen, in denen MySQL-Langsamkeit entstehen kann. Die erste Möglichkeit besteht darin, dass Abfragen vorhanden sind, die keine ordnungsgemäße Indizierung verwenden. Wenn eine Abfrage keinen Index verwenden kann, muss der MySQL-Server mehr Ressourcen und Zeit verwenden, um diese Abfrage zu verarbeiten. Durch die Überwachung von Abfragen haben Sie die Möglichkeit, den SQL-Code zu lokalisieren, der die Hauptursache für eine Verlangsamung ist, und ihn zu beheben, bevor sich die Gesamtleistung verschlechtert.
In diesem Blogbeitrag werden wir die in ClusterControl verfügbare Funktion Query Outlier hervorheben und sehen, wie sie uns helfen kann, die Datenbankleistung zu verbessern. Im Allgemeinen führt ClusterControl das Sampling von MySQL-Abfragen auf zwei Arten durch:
- Abfragen aus dem Leistungsschema abrufen (empfohlen ).
- Parsen Sie den Inhalt von MySQL Slow Query.
Wenn das Leistungsschema deaktiviert ist, verwendet ClusterControl standardmäßig das Protokoll für langsame Abfragen. Um mehr darüber zu erfahren, wie ClusterControl dies durchführt, lesen Sie diesen Blog-Beitrag, How to use the ClusterControl Query Monitor for MySQL, MariaDB and Percona Server.
Was sind Abfrageausreißer?
Ein Ausreißer ist eine Abfrage, die länger dauert als die normale Abfragezeit dieses Typs. Nehmen Sie dies nicht wörtlich als "schlecht geschriebene" Abfragen. Es sollte als potenzielle suboptimale häufige Abfragen behandelt werden, die verbessert werden könnten. Nach einer Reihe von Stichproben und wenn ClusterControl genügend Statistiken hat, kann es feststellen, ob die Latenz höher als normal ist (2 Sigmas + average_query_time), dann ist es ein Ausreißer und wird dem Abfrage-Ausreißer hinzugefügt.
Diese Funktion ist abhängig von der Funktion "Top Queries". Wenn die Abfrageüberwachung aktiviert ist und die Top-Abfragen erfasst und ausgefüllt werden, fassen die Abfrageausreißer diese zusammen und stellen einen auf dem Zeitstempel basierenden Filter bereit. Um die Liste der Abfragen anzuzeigen, die Ihre Aufmerksamkeit erfordern, gehen Sie zu ClusterControl -> Query Monitor -> Query Outliers und Sie sollten einige Abfragen aufgelistet sehen (falls vorhanden):
Wie Sie dem Screenshot oben entnehmen können, handelt es sich bei den Ausreißern im Wesentlichen um Abfragen dauerte mindestens zweimal länger als die durchschnittliche Abfragezeit. Zuerst der erste Eintrag, die durchschnittliche Zeit beträgt 34,41 ms, während die Abfragezeit des Ausreißers 140 ms beträgt (mehr als zweimal höher als die durchschnittliche Zeit). In ähnlicher Weise sind für die nächsten Einträge die Spalten Query Time und Avg Query Time zwei wichtige Dinge, um die Ausstände einer bestimmten Ausreißerabfrage zu rechtfertigen.
Es ist relativ einfach, ein Muster eines bestimmten Abfrageausreißers zu finden, indem man sich einen größeren Zeitraum ansieht, z. B. vor einer Woche, wie im folgenden Screenshot hervorgehoben:
Indem Sie auf jede Zeile klicken, können Sie die vollständige Abfrage sehen, die wirklich ist hilfreich, um das Problem zu lokalisieren und zu verstehen, wie im nächsten Abschnitt gezeigt.
Ausreißer der Abfrage beheben
Um die Ausreißer zu beheben, müssen wir die Art der Abfrage, die Speicher-Engine der Tabellen, die Datenbankversion, den Clustertyp und die Auswirkung der Abfrage verstehen. In einigen Fällen beeinträchtigt die Ausreißerabfrage die Gesamtleistung der Datenbank nicht wirklich. Wie in diesem Beispiel haben wir gesehen, dass die Abfrage die ganze Woche über aufgefallen ist und der einzige Abfragetyp war, der erfasst wurde. Daher ist es wahrscheinlich eine gute Idee, diese Abfrage nach Möglichkeit zu korrigieren oder zu verbessern.
Wie in unserem Fall lautet die Ausreißerabfrage:
SELECT i2l.country_code AS country_code, i2l.country_name AS country_name
FROM ip2location i2l
WHERE (i2l.ip_to >= INET_ATON('104.144.171.139')
AND i2l.ip_from <= INET_ATON('104.144.171.139'))
LIMIT 1
OFFSET 0;
Und das Abfrageergebnis ist:
+--------------+---------------+
| country_code | country_name |
+--------------+---------------+
| US | United States |
+--------------+---------------+
Erklären verwenden
Die Abfrage ist eine schreibgeschützte Bereichsauswahlabfrage, um die geografischen Standortinformationen des Benutzers (Ländercode und Ländername) für eine IP-Adresse in der Tabelle ip2location zu ermitteln. Die Verwendung der EXPLAIN-Anweisung kann uns helfen, den Abfrageausführungsplan zu verstehen:
mysql> EXPLAIN SELECT i2l.country_code AS country_code, i2l.country_name AS country_name
FROM ip2location i2l
WHERE (i2l.ip_to>=INET_ATON('104.144.171.139')
AND i2l.ip_from<=INET_ATON('104.144.171.139'))
LIMIT 1 OFFSET 0;
+----+-------------+-------+------------+-------+--------------------------------------+-------------+---------+------+-------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+--------------------------------------+-------------+---------+------+-------+----------+------------------------------------+
| 1 | SIMPLE | i2l | NULL | range | idx_ip_from,idx_ip_to,idx_ip_from_to | idx_ip_from | 5 | NULL | 66043 | 50.00 | Using index condition; Using where |
+----+-------------+-------+------------+-------+--------------------------------------+-------------+---------+------+-------+----------+------------------------------------+
Die Abfrage wird mit einem Bereichsscan für die Tabelle unter Verwendung des Index idx_ip_from mit 50 % potenziellen Zeilen (gefiltert) ausgeführt.
Richtige Speicher-Engine
Blick auf die Tabellenstruktur von ip2location:
mysql> SHOW CREATE TABLE ip2location\G
*************************** 1. row ***************************
Table: ip2location
Create Table: CREATE TABLE `ip2location` (
`ip_from` int(10) unsigned DEFAULT NULL,
`ip_to` int(10) unsigned DEFAULT NULL,
`country_code` char(2) COLLATE utf8_bin DEFAULT NULL,
`country_name` varchar(64) COLLATE utf8_bin DEFAULT NULL,
KEY `idx_ip_from` (`ip_from`),
KEY `idx_ip_to` (`ip_to`),
KEY `idx_ip_from_to` (`ip_from`,`ip_to`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
Diese Tabelle basiert auf der IP2location-Datenbank und wird selten aktualisiert/geschrieben, normalerweise nur am ersten Tag des Kalendermonats (vom Anbieter empfohlen). Eine Option besteht also darin, die Tabelle in die Speicher-Engine MyISAM (MySQL) oder Aria (MariaDB) mit festem Zeilenformat zu konvertieren, um eine bessere schreibgeschützte Leistung zu erzielen. Beachten Sie, dass dies nur zutrifft, wenn Sie MySQL oder MariaDB eigenständig oder repliziert ausführen. Bleiben Sie bei der Cluster- und Gruppenreplikation von Galera bitte bei der InnoDB-Speicher-Engine (es sei denn, Sie wissen, was Sie tun).
Wie auch immer, um die Tabelle von InnoDB zu MyISAM mit festem Zeilenformat zu konvertieren, führen Sie einfach den folgenden Befehl aus:
ALTER TABLE ip2location ENGINE=MyISAM ROW_FORMAT=FIXED;
In unserer Messung mit 1000 zufälligen IP-Adressen-Lookup-Tests verbesserte sich die Abfrageleistung mit MyISAM und festem Zeilenformat um etwa 20 %:
- Durchschnittliche Zeit (InnoDB):21,467823 ms
- Durchschnittliche Zeit (MyISAM Fixed):17,175942 ms
- Verbesserung:19,992157565301 %
Sie können davon ausgehen, dass dieses Ergebnis unmittelbar nach der Änderung der Tabelle angezeigt wird. Auf der höheren Ebene (Anwendung/Load Balancer) ist keine Änderung erforderlich.
Abstimmung der Abfrage
Eine andere Möglichkeit besteht darin, den Abfrageplan zu überprüfen und einen effizienteren Ansatz für einen besseren Abfrageausführungsplan zu verwenden. Dieselbe Abfrage kann auch mit der folgenden Unterabfrage geschrieben werden:
SELECT `country_code`, `country_name` FROM
(SELECT `country_code`, `country_name`, `ip_from`
FROM `ip2location`
WHERE ip_to >= INET_ATON('104.144.171.139')
LIMIT 1)
AS temptable
WHERE ip_from <= INET_ATON('104.144.171.139');
Die optimierte Abfrage hat den folgenden Abfrageausführungsplan:
mysql> EXPLAIN SELECT `country_code`,`country_name` FROM
(SELECT `country_code`, `country_name`, `ip_from`
FROM `ip2location`
WHERE ip_to >= INET_ATON('104.144.171.139')
LIMIT 1)
AS temptable
WHERE ip_from <= INET_ATON('104.144.171.139');
+----+-------------+--------------+------------+--------+---------------+-----------+---------+------+-------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+--------+---------------+-----------+---------+------+-------+----------+-----------------------+
| 1 | PRIMARY | <derived2> | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
| 2 | DERIVED | ip2location | NULL | range | idx_ip_to | idx_ip_to | 5 | NULL | 66380 | 100.00 | Using index condition |
+----+-------------+--------------+------------+--------+---------------+-----------+---------+------+-------+----------+-----------------------+
Mit Unterabfragen können wir die Abfrage optimieren, indem wir eine abgeleitete Tabelle verwenden, die sich auf einen Index konzentriert. Die Abfrage sollte nur 1 Datensatz zurückgeben, bei dem der ip_to-Wert größer oder gleich dem IP-Adresswert ist. Dadurch können die potenziellen Zeilen (gefiltert) 100 % erreichen, was am effizientesten ist. Überprüfen Sie dann, ob ip_from kleiner oder gleich dem Wert der IP-Adresse ist. Wenn ja, dann sollten wir die Aufzeichnung finden. Andernfalls existiert die IP-Adresse nicht in der ip2location-Tabelle.
Bei unserer Messung verbesserte sich die Abfrageleistung mit einer Unterabfrage um etwa 99 %:
- Durchschnittliche Zeit (InnoDB + Bereichsscan):22,87112 ms
- Durchschnittliche Zeit (InnoDB + Unterabfrage):0,14744 ms
- Verbesserung:99,355344207017 %
Mit der obigen Optimierung sehen wir eine Abfrageausführungszeit von weniger als einer Millisekunde für diese Art von Abfrage, was eine massive Verbesserung darstellt, wenn man bedenkt, dass die vorherige durchschnittliche Zeit 22 ms betrug. Wir müssen jedoch einige Änderungen an der höheren Ebene (Anwendung/Load Balancer) vornehmen, um von dieser optimierten Abfrage zu profitieren.
Patching oder Abfrageumschreibung
Patchen Sie Ihre Anwendungen so, dass sie die optimierte Abfrage verwenden, oder schreiben Sie die Ausreißerabfrage um, bevor sie den Datenbankserver erreicht. Wir können dies erreichen, indem wir einen MySQL-Load-Balancer wie ProxySQL (Abfrageregeln) oder MariaDB MaxScale (Filter zum Umschreiben von Anweisungen) oder das MySQL Query Rewriter-Plugin verwenden. Im folgenden Beispiel verwenden wir ProxySQL vor unserem Datenbank-Cluster und können einfach eine Regel erstellen, um die langsamere Abfrage in die schnellere umzuschreiben, zum Beispiel:
Speichern Sie die Abfrageregel und überwachen Sie die Seite "Abfrageausreißer" in ClusterControl. Dieser Fix entfernt offensichtlich die Ausreißerabfragen aus der Liste, nachdem die Abfrageregel aktiviert wurde.
Fazit
Query Outliers ist ein proaktives Abfrageüberwachungstool, das uns dabei helfen kann, das Leistungsproblem zu verstehen und zu beheben, bevor es außer Kontrolle gerät. Wenn Ihre Anwendung wächst und anspruchsvoller wird, kann Ihnen dieses Tool dabei helfen, eine anständige Datenbankleistung aufrechtzuerhalten.