Langzame zoekopdrachten, inefficiënte zoekopdrachten of langlopende zoekopdrachten zijn problemen die DBA's regelmatig teisteren. Ze zijn altijd alomtegenwoordig, maar zijn een onvermijdelijk onderdeel van het leven van iedereen die verantwoordelijk is voor het beheer van een database.
Slecht database-ontwerp kan de efficiëntie van de query en de prestaties ervan beïnvloeden. Gebrek aan kennis of oneigenlijk gebruik van functieaanroepen, opgeslagen procedures of routines kan ook leiden tot verslechtering van de databaseprestaties en kan zelfs het hele MySQL-databasecluster schaden.
Voor een master-slave-replicatie is een veelvoorkomende oorzaak van deze problemen tabellen die geen primaire of secundaire indexen hebben. Dit veroorzaakt een slaafvertraging die erg lang kan duren (in het ergste geval).
In deze tweedelige serieblog geven we u een opfriscursus over hoe u het maximaliseren van uw databasequery's in MySQL kunt aanpakken om de efficiëntie en prestaties te verbeteren.
Voeg altijd een unieke index toe aan uw tabel
Tabellen die geen primaire of unieke sleutels hebben, veroorzaken doorgaans enorme problemen wanneer gegevens groter worden. Wanneer dit gebeurt, kan een eenvoudige wijziging van de gegevens de database blokkeren. Het ontbreken van de juiste indexen en een UPDATE- of DELETE-instructie is toegepast op de specifieke tabel, een volledige tabelscan zal door MySQL worden gekozen als het queryplan. Dat kan hoge schijf-I/O veroorzaken voor lezen en schrijven en de prestaties van uw database verminderen. Zie hieronder een voorbeeld:
root[test]> show create table sbtest2\G
*************************** 1. row ***************************
Table: sbtest2
Create Table: CREATE TABLE `sbtest2` (
`id` int(10) unsigned NOT NULL,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
root[test]> explain extended update sbtest2 set k=52, pad="xx234xh1jdkHdj234" where id=57;
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | UPDATE | sbtest2 | NULL | ALL | NULL | NULL | NULL | NULL | 1923216 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.06 sec)
Terwijl een tabel met primaire sleutel een zeer goed zoekplan heeft,
root[test]> show create table sbtest3\G
*************************** 1. row ***************************
Table: sbtest3
Create Table: CREATE TABLE `sbtest3` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2097121 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
root[test]> explain extended update sbtest3 set k=52, pad="xx234xh1jdkHdj234" where id=57;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | UPDATE | sbtest3 | NULL | range | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Primaire of unieke sleutels vormen een essentieel onderdeel voor een tabelstructuur, omdat dit erg belangrijk is, vooral bij het uitvoeren van onderhoud aan een tafel. Het gebruik van tools uit de Percona Toolkit (zoals pt-online-schema-change of pt-table-sync) raadt u bijvoorbeeld aan om over unieke sleutels te beschikken. Houd er rekening mee dat de PRIMARY KEY al een unieke sleutel is en dat een primaire sleutel geen NULL-waarden kan bevatten, maar een unieke sleutel. Het toewijzen van een NULL-waarde aan een primaire sleutel kan een fout veroorzaken zoals,
ERROR 1171 (42000): All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead
Voor slave-knooppunten is het ook gebruikelijk dat in bepaalde gevallen de primaire/unieke sleutel niet aanwezig is in de tabel, wat daarom een discrepantie is van de tabelstructuur. U kunt mysqldiff gebruiken om dit te bereiken of u kunt mysqldump --no-data ... params en een diff uitvoeren om de tabelstructuur te vergelijken en te controleren of er enige discrepantie is.
Scan tabellen met dubbele indexen en laat het vallen
Dubbele indices kunnen ook prestatievermindering veroorzaken, vooral wanneer de tabel een groot aantal records bevat. MySQL moet meerdere pogingen doen om de query te optimaliseren en voert meer queryplannen uit om te controleren. Het omvat het scannen van grote indexdistributie of statistieken en dat voegt prestatieoverhead toe omdat het geheugenconflicten of een hoog I/O-geheugengebruik kan veroorzaken.
Degradatie voor zoekopdrachten wanneer dubbele indices worden waargenomen in een tabel, heeft ook kenmerken voor het verzadigen van de bufferpool. Dit kan ook van invloed zijn op de prestaties van MySQL wanneer de checkpointing de transactielogboeken naar de schijf spoelt. Dit komt door het verwerken en opslaan van een ongewenste index (wat in feite een verspilling van ruimte is in de specifieke tabelruimte van die tabel). Houd er rekening mee dat dubbele indices ook worden opgeslagen in de tabelruimte die ook in de bufferpool moet worden opgeslagen.
Bekijk de onderstaande tabel die meerdere dubbele sleutels bevat:
root[test]#> show create table sbtest3\G
*************************** 1. row ***************************
Table: sbtest3
Create Table: CREATE TABLE `sbtest3` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k` (`k`,`pad`,`c`),
KEY `kcp2` (`id`,`k`,`c`,`pad`),
KEY `kcp` (`k`,`c`,`pad`),
KEY `pck` (`pad`,`c`,`id`,`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2048561 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
en heeft een grootte van 2,3 GiB
root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd
2.3G /var/lib/mysql/test/sbtest3.ibd
Laten we de dubbele indices verwijderen en de tabel opnieuw opbouwen met een no-op alter,
root[test]#> drop index kcp2 on sbtest3; drop index kcp on sbtest3 drop index pck on sbtest3;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
root[test]#> alter table sbtest3 engine=innodb;
Query OK, 0 rows affected (28.23 sec)
Records: 0 Duplicates: 0 Warnings: 0
root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd
945M /var/lib/mysql/test/sbtest3.ibd
Het heeft tot ~59% van de oude grootte van de tafelruimte kunnen besparen, wat echt enorm is.
Om dubbele indexen te bepalen, kunt u pt-duplicate-checker gebruiken om de taak voor u af te handelen.
Uw bufferpool aanpassen
Voor deze sectie verwijs ik alleen naar de InnoDB-opslagengine.
Bufferpool is een belangrijk onderdeel binnen de InnoDB-kernelruimte. Dit is waar InnoDB tabel- en indexgegevens in de cache opslaat wanneer ze worden geopend. Het versnelt de verwerking omdat veelgebruikte gegevens efficiënt in het geheugen worden opgeslagen met behulp van BTREE. Als u bijvoorbeeld meerdere tabellen heeft die bestaan uit>=100GiB en veel toegang krijgt, raden we u aan een snel vluchtig geheugen te delegeren vanaf een grootte van 128GiB en de bufferpool toe te wijzen met 80% van het fysieke geheugen. De 80% moet efficiënt worden bewaakt. U kunt SHOW ENGINE INNODB STATUS \G gebruiken of u kunt gebruikmaken van monitoringsoftware zoals ClusterControl die een fijnmazige monitoring biedt met een bufferpool en de relevante gezondheidsstatistieken. Stel ook de variabele innodb_buffer_pool_instances dienovereenkomstig in. U kunt dit groter dan 8 instellen (standaard indien innodb_buffer_pool_size>=1GiB), zoals 16, 24, 32 of 64 of hoger indien nodig.
Bij het bewaken van de bufferpool, moet u de globale statusvariabele Innodb_buffer_pool_pages_free controleren, die u gedachten geeft als het nodig is om de bufferpool aan te passen, of misschien overwegen of er ook ongewenste of dubbele indexen zijn die de buffer. De SHOW ENGINE INNODB STATUS \G biedt ook een meer gedetailleerd aspect van de bufferpoolinformatie, inclusief de individuele bufferpool, gebaseerd op het aantal innodb_buffer_pool_instances dat je hebt ingesteld.
Gebruik FULLTEXT-indexen (maar alleen indien van toepassing)
Vragen gebruiken zoals,
SELECT bookid, page, context FROM books WHERE context like '%for dummies%';
waarin context een string-type (char, varchar, text) kolom is, is dit een voorbeeld van een superslechte query! Het trekken van grote inhoud van records met een filter dat hebzuchtig moet zijn, eindigt met een volledige tabelscan, en dat is gewoon gek. Overweeg het gebruik van de FULLTEXT-index. A FULLTEXT indexen hebben een omgekeerd indexontwerp. Omgekeerde indexen slaan een lijst met woorden op en voor elk woord een lijst met documenten waarin het woord voorkomt. Om zoeken op nabijheid te ondersteunen, wordt ook positie-informatie voor elk woord opgeslagen, als byte-offset.
Om FULLTEXT te gebruiken voor het zoeken of filteren van gegevens, moet je de combinatie van de MATCH() ...AGAINST-syntaxis gebruiken en niet zoals de bovenstaande query. Natuurlijk moet u het veld specificeren als uw FULLTEXT-indexveld.
Om een FULLTEXT-index te maken, geeft u FULLTEXT op als uw index. Zie onderstaand voorbeeld:
root[minime]#> CREATE FULLTEXT INDEX aboutme_fts ON users_info(aboutme);
Query OK, 0 rows affected, 1 warning (0.49 sec)
Records: 0 Duplicates: 0 Warnings: 1
root[jbmrcd_date]#> show warnings;
+---------+------+--------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------+
| Warning | 124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+
1 row in set (0.00 sec)
Hoewel het gebruik van FULLTEXT-indexen voordelen kan bieden bij het zoeken naar woorden binnen een zeer grote context in een kolom, veroorzaakt het ook problemen bij onjuist gebruik.
Wanneer u een FULLTEXT-zoekopdracht uitvoert naar een grote tabel die constant wordt geopend (waarbij een aantal klantverzoeken naar verschillende, unieke zoekwoorden zoekt), kan dit zeer CPU-intensief zijn.
Er zijn ook bepaalde gelegenheden dat FULLTEXT niet van toepassing is. Zie deze externe blogpost. Hoewel ik dit niet met 8.0 heb geprobeerd, zie ik geen wijzigingen die hier relevant voor zijn. We raden aan om FULLTEXT niet te gebruiken voor het zoeken in een big data-omgeving, vooral niet voor tabellen met veel verkeer. Probeer anders gebruik te maken van andere technologieën zoals Apache Lucene, Apache Solr, tsearch2 of Sphinx.
Vermijd het gebruik van NULL in kolommen
Kolommen die null-waarden bevatten, zijn prima in MySQL. Maar als u kolommen met null-waarden in een index gebruikt, kan dit van invloed zijn op de queryprestaties, omdat de optimizer niet het juiste queryplan kan bieden vanwege een slechte indexdistributie. Er zijn echter bepaalde manieren om zoekopdrachten met null-waarden te optimaliseren, maar natuurlijk, als dit aan de vereisten voldoet. Raadpleeg de documentatie van MySQL over Null Optimization. Je kunt ook dit externe bericht bekijken, wat ook nuttig is.
Ontwerp efficiënt uw schematopologie en tabellenstructuur
Tot op zekere hoogte biedt het normaliseren van uw databasetabellen van 1NF (First Normal Form) naar 3NF (Third Normal Form) enig voordeel voor de efficiëntie van query's, omdat genormaliseerde tabellen de neiging hebben om overbodige records te vermijden. Een goede planning en ontwerp voor uw tabellen is erg belangrijk omdat u op deze manier gegevens kunt ophalen of ophalen en aan elk van deze acties zijn kosten verbonden. Bij genormaliseerde tabellen is het doel van de database ervoor te zorgen dat elke niet-sleutelkolom in elke tabel direct afhankelijk is van de sleutel; de hele sleutel en niets anders dan de sleutel. Als dit doel wordt bereikt, betaalt het de voordelen terug in de vorm van minder ontslagen, minder afwijkingen en verbeterde efficiëntie.
Hoewel het normaliseren van uw tabellen veel voordelen heeft, betekent dit niet dat u al uw tabellen op deze manier moet normaliseren. U kunt een ontwerp voor uw database implementeren met behulp van Star Schema. Het ontwerpen van uw tabellen met behulp van Star Schema heeft het voordeel van eenvoudigere query's (vermijd complexe cross-joins), gemakkelijk op te halen gegevens voor rapportage, biedt prestatieverbeteringen omdat het niet nodig is om unions of complexe joins te gebruiken, of snelle aggregaties. Een Star Schema is eenvoudig te implementeren, maar u moet zorgvuldig plannen omdat het grote problemen en nadelen kan opleveren wanneer uw tafel groter wordt en onderhoud vereist. Star Schema (en de onderliggende tabellen) zijn gevoelig voor problemen met gegevensintegriteit, dus de kans is groot dat een heleboel gegevens overbodig zijn. Als u denkt dat deze tabel constant moet zijn (structuur en ontwerp) en is ontworpen om de efficiëntie van query's te benutten, dan is deze tabel ideaal voor deze aanpak.
Het mixen van uw databaseontwerpen (zolang u maar kunt bepalen en identificeren wat voor soort gegevens op uw tabellen moeten worden opgehaald) is erg belangrijk omdat u kunt profiteren van efficiëntere query's en help de DBA met back-ups, onderhoud en herstel.
Verwijder constante en oude gegevens
We hebben onlangs enkele best practices geschreven voor het archiveren van uw database in de cloud. Het behandelt hoe u kunt profiteren van gegevensarchivering voordat het naar de cloud gaat. Dus hoe helpt het verwijderen van oude gegevens of het archiveren van uw constante en oude gegevens de efficiëntie van query's? Zoals in mijn vorige blog vermeld, zijn er voordelen voor grotere tabellen die voortdurend worden gewijzigd en ingevoegd met nieuwe gegevens, de tabelruimte kan snel groeien. MySQL en InnoDB werken efficiënt wanneer records of gegevens aan elkaar grenzen en betekenis hebben voor de volgende rij in de tabel. Dit betekent dat als u geen oude records heeft die niet meer gebruikt hoeven te worden, de optimizer die niet hoeft op te nemen in de statistieken die een veel efficiënter resultaat opleveren. Logisch toch? En de efficiëntie van query's ligt niet alleen aan de applicatiekant, maar moet ook rekening houden met de efficiëntie ervan bij het uitvoeren van een back-up en bij onderhoud of failover. Als u bijvoorbeeld een slechte en lange query heeft die van invloed kan zijn op uw onderhoudsperiode of een failover, kan dat een probleem zijn.
Schakel het loggen van zoekopdrachten in indien nodig
Stel het logbestand voor trage zoekopdrachten van MySQL altijd in op uw persoonlijke behoeften. Als u Percona Server gebruikt, kunt u profiteren van hun uitgebreide trage registratie van query's. Hiermee kunt u op de gebruikelijke manier bepaalde variabelen definiëren. U kunt typen query's in combinatie filteren, zoals full_scan, full_join, tmp_table, enz. U kunt ook de snelheid van langzame registratie van query's dicteren via de variabele log_slow_rate_type en vele andere.
Het belang van het inschakelen van het loggen van zoekopdrachten in MySQL (zoals langzame zoekopdrachten) is gunstig voor het inspecteren van uw zoekopdrachten, zodat u uw MySQL kunt optimaliseren of afstemmen door bepaalde variabelen aan te passen aan uw vereisten. Zorg ervoor dat deze variabelen zijn ingesteld om een trage querylog in te schakelen:
- long_query_time - wijs de juiste waarde toe voor hoe lang de query's kunnen duren. Als de query's langer dan 10 seconden duren (standaard), wordt deze in het logbestand voor trage query's opgeslagen dat u hebt toegewezen.
- slow_query_log - om het in te schakelen, zet het op 1.
- slow_query_log_file - dit is het bestemmingspad voor uw logbestand met trage query's.
Het trage query-logboek is erg handig voor query-analyse en het diagnosticeren van slechte query's die vastlopen, slavevertragingen, langlopende query's, geheugen- of CPU-intensief veroorzaken, of zelfs de server doen crashen. Als u pt-query-digest of pt-index-usage gebruikt, gebruik dan het logbestand voor trage zoekopdrachten als uw brondoel voor het rapporteren van deze zoekopdrachten.
Conclusie
We hebben in deze blog enkele manieren besproken die u kunt gebruiken om de efficiëntie van databasequery's te maximaliseren. In dit volgende deel bespreken we nog meer factoren die u kunnen helpen de prestaties te maximaliseren. Blijf op de hoogte!