De werkbelasting van de MySQL-database wordt bepaald door het aantal query's dat wordt verwerkt. Er zijn verschillende situaties waarin MySQL traagheid kan ontstaan. De eerste mogelijkheid is als er query's zijn die niet de juiste indexering gebruiken. Wanneer een query geen gebruik kan maken van een index, moet de MySQL-server meer bronnen en tijd gebruiken om die query te verwerken. Door query's te bewaken, kunt u de SQL-code lokaliseren die de hoofdoorzaak is van een vertraging en deze oplossen voordat de algehele prestaties achteruitgaan.
In deze blogpost gaan we de Query Outlier-functie die beschikbaar is in ClusterControl onder de aandacht brengen en zien hoe deze ons kan helpen de databaseprestaties te verbeteren. Over het algemeen voert ClusterControl op twee manieren MySQL-querysteekproeven uit:
- De query's ophalen uit het prestatieschema (aanbevolen ).
- Ontdek de inhoud van MySQL Slow Query.
Als het prestatieschema is uitgeschakeld, gaat ClusterControl standaard naar het logbestand voor trage query's. Om meer te weten te komen over hoe ClusterControl dit doet, lees je deze blogpost, How to use the ClusterControl Query Monitor for MySQL, MariaDB and Percona Server.
Wat zijn uitschieters voor zoekopdrachten?
Een uitbijter is een zoekopdracht die langer duurt dan de normale zoekopdracht van dat type. Vat dit niet letterlijk op als "slecht geschreven" vragen. Het moet worden behandeld als mogelijke suboptimale veelvoorkomende zoekopdrachten die kunnen worden verbeterd. Na een aantal steekproeven en wanneer ClusterControl voldoende statistieken heeft, kan het bepalen of de latentie hoger is dan normaal (2 sigma's + gemiddelde_query_time), dan is het een uitbijter en wordt deze toegevoegd aan de Query-uitbijter.
Deze functie is afhankelijk van de functie Topquery's. Als Querybewaking is ingeschakeld en Topquery's worden vastgelegd en ingevuld, zullen de Query-uitschieters deze samenvatten en een filter bieden op basis van tijdstempel. Ga naar ClusterControl -> Query Monitor -> Query Outliers om de lijst met query's te zien die aandacht vereisen en er zouden enkele query's moeten worden weergegeven (indien aanwezig):
Zoals je kunt zien in de bovenstaande schermafbeelding, zijn de uitbijters in feite vragen die duurde minstens 2 keer langer dan de gemiddelde zoektijd. Eerst de eerste invoer, de gemiddelde tijd is 34,41 ms, terwijl de zoektijd van de uitbijter 140 ms is (meer dan 2 keer hoger dan de gemiddelde tijd). Evenzo zijn voor de volgende items de kolommen Query Time en Avg Query Time twee belangrijke dingen om de uitstaande bedragen van een bepaalde uitbijterquery te rechtvaardigen.
Het is relatief eenvoudig om een patroon van een bepaalde uitbijter van een zoekopdracht te vinden door naar een grotere tijdsperiode te kijken, zoals een week geleden, zoals aangegeven in de volgende schermafbeelding:
Door op elke rij te klikken, kunt u de volledige zoekopdracht zien, die in werkelijkheid nuttig om het probleem te lokaliseren en te begrijpen, zoals weergegeven in het volgende gedeelte.
De uitschieters voor zoekopdrachten corrigeren
Om de uitbijters te corrigeren, moeten we de aard van de query, de opslagengine van de tabellen, de databaseversie, het type clustering en de impact van de query begrijpen. In sommige gevallen verslechtert de uitbijterquery niet echt de algehele databaseprestaties. Net als in dit voorbeeld hebben we gezien dat de zoekopdracht al de hele week opvalt en dat dit het enige type zoekopdracht was dat werd vastgelegd, dus het is waarschijnlijk een goed idee om deze zoekopdracht indien mogelijk op te lossen of te verbeteren.
Zoals in ons geval is de uitbijter-query:
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;
En het resultaat van de zoekopdracht is:
+--------------+---------------+
| country_code | country_name |
+--------------+---------------+
| US | United States |
+--------------+---------------+
Uitleg gebruiken
De query is een alleen-lezen bereikselectiequery om de geografische locatie-informatie van de gebruiker (landcode en landnaam) te bepalen voor een IP-adres op tafel ip2location. Het gebruik van de instructie EXPLAIN kan ons helpen het uitvoeringsplan van de query te begrijpen:
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 |
+----+-------------+-------+------------+-------+--------------------------------------+-------------+---------+------+-------+----------+------------------------------------+
De zoekopdracht wordt uitgevoerd met een bereikscan op de tabel met behulp van index idx_ip_from met 50% potentiële rijen (gefilterd).
Juiste opslagengine
Kijkend naar de tabelstructuur van 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
Deze tabel is gebaseerd op de IP2-locatiedatabase en wordt zelden bijgewerkt/geschreven, meestal alleen op de eerste dag van de kalendermaand (aanbevolen door de leverancier). Een optie is dus om de tabel te converteren naar MyISAM (MySQL) of Aria (MariaDB) opslagengine met een vast rijformaat om betere alleen-lezen prestaties te krijgen. Merk op dat dit alleen van toepassing is als u op MySQL of MariaDB standalone of replicatie draait. Voor Galera-cluster- en groepsreplicatie houdt u zich aan de InnoDB-opslagengine (tenzij u weet wat u doet).
Hoe dan ook, om de tabel te converteren van InnoDB naar MyISAM met vaste rij-indeling, voert u gewoon de volgende opdracht uit:
ALTER TABLE ip2location ENGINE=MyISAM ROW_FORMAT=FIXED;
In onze meting, met 1000 willekeurige opzoektests voor IP-adressen, verbeterden de queryprestaties met ongeveer 20% met MyISAM en vaste rij-indeling:
- Gemiddelde tijd (InnoDB):21.467823 ms
- Gemiddelde tijd (MyISAM Fixed):17.175942 ms
- Verbetering:19.992157565301 %
U kunt dit resultaat verwachten onmiddellijk nadat de tabel is gewijzigd. Er is geen wijziging op het hogere niveau (applicatie/load balancer) nodig.
De zoekopdracht afstemmen
Een andere manier is om het queryplan te inspecteren en een efficiëntere aanpak te gebruiken voor een beter plan voor het uitvoeren van query's. Dezelfde zoekopdracht kan ook worden geschreven met behulp van een subquery zoals hieronder:
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');
De afgestemde query heeft het volgende uitvoeringsplan voor de query:
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 |
+----+-------------+--------------+------------+--------+---------------+-----------+---------+------+-------+----------+-----------------------+
Met behulp van subquery kunnen we de query optimaliseren door een afgeleide tabel te gebruiken die zich op één index richt. De query moet slechts 1 record retourneren waarbij de ip_to-waarde groter is dan of gelijk is aan de IP-adreswaarde. Hierdoor kunnen de potentiële rijen (gefilterd) 100% bereiken, wat het meest efficiënt is. Controleer vervolgens of de ip_from kleiner is dan of gelijk is aan de waarde van het IP-adres. Als dat zo is, moeten we het record vinden. Anders bestaat het IP-adres niet in de ip2location-tabel.
In onze meting verbeterden de queryprestaties met ongeveer 99% met behulp van een subquery:
- Gemiddelde tijd (InnoDB + bereikscan):22,87112 ms
- Gemiddelde tijd (InnoDB + subquery):0.14744 ms
- Verbetering:99,355344207017 %
Met de bovenstaande optimalisatie kunnen we een query-uitvoeringstijd van minder dan een milliseconde van dit type query zien, wat een enorme verbetering is, aangezien de vorige gemiddelde tijd 22 ms was. We moeten echter enkele wijzigingen aanbrengen in het hogere niveau (applicatie/load balancer) om te profiteren van deze afgestemde zoekopdracht.
Patchen of herschrijven van query's
Patch uw toepassingen om de afgestemde query te gebruiken of herschrijf de uitbijterquery voordat deze de databaseserver bereikt. We kunnen dit bereiken door een MySQL-load balancer te gebruiken, zoals ProxySQL (queryregels) of MariaDB MaxScale (filter voor het herschrijven van instructies), of door de MySQL Query Rewriter-plug-in te gebruiken. In het volgende voorbeeld gebruiken we ProxySQL voor ons databasecluster en we kunnen eenvoudig een regel maken om de langzamere query te herschrijven in de snellere, bijvoorbeeld:
Sla de queryregel op en controleer de pagina Query Outliers in ClusterControl. Deze oplossing verwijdert uiteraard de uitbijterquery's uit de lijst nadat de queryregel is geactiveerd.
Conclusie
Query-uitbijters is een proactieve tool voor het bewaken van query's die ons kan helpen het prestatieprobleem te begrijpen en op te lossen voordat het uit de hand loopt. Naarmate uw toepassing groeit en veeleisender wordt, kan deze tool u helpen om een behoorlijke databaseprestatie te behouden.