MySQL-indexering: van 2‑seconden zoekopdrachten naar resultaten in 30 ms
De grootste backend-snelheidswinst die ik in echte projecten zie, is ook de meest over het hoofd geziene: database-indexering. Eén goed ontworpen index kan een productzoekopdracht van 2 seconden veranderen in een respons van 30 ms — een verbetering van 98% die je bezoekers meteen voelen. Toch leunen de meeste WordPress-sites alleen op standaardindexen, en veel custom PHP-apps worden gebouwd zonder enige indexeringsstrategie. De oplossing is geen extra hardware, maar je database precies leren waar te kijken. Deze gids laat zien hoe je indexen ontwerpt die zelfs een grote catalogus direct laten aanvoelen.
Waarom indexering belangrijk is voor webprestaties
Wanneer een gebruiker een pagina bezoekt die een database bevraagt (productlijsten, zoekresultaten, gebruikersprofielen), moet MySQL de relevante rijen vinden. Zonder indexen voert MySQL een volledige tabelscan uit – elke rij lezen totdat overeenkomsten zijn gevonden. Voor een tabel met 100.000 rijen duurt een volledige scan 200‑500 ms. Bij 1.000 gelijktijdige gebruikers wordt dat seconden vertraging en raakt de server-CPU overbelast.
Indexen zijn als de index van een boek: ze vertellen MySQL precies waar rijen te vinden zijn, waardoor een O(n)-operatie wordt omgezet in O(log n). Het resultaat zijn query’s die in milliseconden in plaats van seconden resultaten opleveren.
Hoe MySQL-indexen werken (in eenvoudige bewoordingen)
MySQL gebruikt standaard B‑Tree-indexen. Ze slaan kolomwaarden op in een gesorteerde boomstructuur. Wanneer u een query uitvoert met een WHERE-clausule op een geïndexeerde kolom, doorloopt MySQL de boom om de overeenkomende waarden te vinden in ongeveer log₂(N) stappen. Voor 1 miljoen rijen zijn dat ongeveer 20 stappen in plaats van 1 miljoen scans.
Belangrijke regel: Indexen helpen bij filteren op gelijkheid (=), bereik (<, >, BETWEEN) of prefixovereenkomst (LIKE 'term%'). Ze kunnen niet helpen bij LIKE '%term%' (leidend jokerteken) of functieaanroepen zoals WHERE DATE(created_at) = '2025-01-01'.
Identificeren van trage query’s – EXPLAIN gebruiken
Voordat u indexen toevoegt, zoekt u uw traagste query’s. Schakel de MySQL slow query log in:
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 0.5; -- log queries slower than 0.5 seconds
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
Voer vervolgens mysqldumpslow -s t /var/log/mysql/slow.log uit om de traagste query’s te zien.
Voeg vervolgens EXPLAIN toe voor elke verdachte query om te zien hoe MySQL deze uitvoert. Bijvoorbeeld:
EXPLAIN SELECT * FROM products WHERE category_id = 5 AND price < 100;
Kijk naar de kolom type:
ALL– volledige tabelscan (slechtst).rangeofref– gebruikt een index (goed).const– primaire sleutelopzoeking (best).
Controleer ook rows – het aantal gescande rijen. Als dit dicht bij het tabeltotaal ligt, voeg dan een index toe.
Praktische indexeringsstrategie – enkele vs samengestelde indexen
Indexen met één kolom
Gebruik ze wanneer u vaak op één kolom filtert.
CREATE INDEX idx_user_id ON orders (user_id);
Samengestelde (multi‑kolom) indexen
Wanneer u op meerdere kolommen filtert, is een samengestelde index veel efficiënter dan afzonderlijke indexen. MySQL kan slechts één index per tabelreferentie gebruiken, maar een samengestelde index bestrijkt meerdere kolommen.
-- Slow: 2 seconds, scans 50,000 rows
SELECT * FROM products WHERE category_id = 5 AND price < 100;
-- Add composite index (order matters!)
CREATE INDEX idx_category_price ON products (category_id, price);
-- Now: 30ms, scans 127 rows
Volgorderegel: Plaats de meest selectieve kolom eerst (degene die de meeste rijen eruit filtert). Meestal reduceert category_id 50.000 → 5.000, daarna reduceert price tot 200. Als u omkeert (price, category_id), zou de index minder efficiënt zijn voor bereikquery’s op prijs.
Coverende indexen (de Heilige Graal)
Als uw query alleen kolommen nodig heeft die in de index staan, kan MySQL de query volledig vanuit de index beantwoorden – zonder de tabelgegevens aan te raken. Dit is extreem snel.
-- Query only needs id and name
SELECT id, name FROM products WHERE category_id = 5;
-- Covering index
CREATE INDEX idx_category_id_name ON products (category_id, name);
-- EXPLAIN will show "Using index" in Extra column
Praktijkvoorbeeld: e-commerce productfiltering – Voor en na
Een online winkel met 50.000 producten had een zoekpagina die filterde op categorie, merk en prijsklasse. De oorspronkelijke query:
SELECT * FROM products
WHERE category_id = 12
AND brand_id IN (3,7,9)
AND price BETWEEN 50 AND 200
ORDER BY price LIMIT 24;
Zonder index: volledige tabelscan – 2,3 seconden, 50.000 rijen gescand.
Na analyse: Ze voegden een samengestelde index toe:
CREATE INDEX idx_category_brand_price ON products (category_id, brand_id, price);
Resultaat: Dezelfde query werd uitgevoerd in 80 ms (96% verbetering) en scant slechts 312 rijen. De laadtijd van de pagina daalde van 3,2 seconden naar 0,6 seconden.
Indexering voor WordPress (als u moet blijven)
WordPress staat bekend om trage query’s vanwege het generieke schema. De tabel wp_postmeta slaat elk aangepast veld op als een sleutel‑waarde-paar, wat leidt tot miljoenen rijen. U kunt aangepaste indexen toevoegen om veelvoorkomende query’s te verbeteren:
-- If you frequently query postmeta by meta_key and meta_value
ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_value (meta_key, meta_value(100));
Maar zelfs met indexering lijdt WordPress nog steeds onder de overhead van wp_query. Op maat gemaakte PHP-apps die zijn ontworpen met een goed schema en indexen, zullen altijd sneller zijn.
Indexen onderhouden – wanneer toevoegen, wanneer verwijderen
- Voeg indexen toe op kolommen die worden gebruikt in
WHERE,JOIN,ORDER BYenGROUP BY. - Verwijder indexen op kolommen die zelden worden gebruikt of een zeer lage kardinaliteit hebben (bijv. een `status`-kolom met slechts twee waarden). Elke index vertraagt
INSERT,UPDATEenDELETEomdat MySQL de index ook moet bijwerken. - Monitor indexgebruik met
SHOW INDEX FROM table;enSELECT * FROM sys.schema_unused_indexes;(MySQL 8.0).
Hulpmiddelen voor indexeringsanalyse
- EXPLAIN – ingebouwd, begin hier altijd.
- MySQL Workbench – visuele EXPLAIN en prestatie dashboard.
- pt‑query‑digest (Percona Toolkit) – analyseert de slow query log en stelt indexen voor.
- phpMyAdmin – “Profiling” functie om details van query-uitvoering te zien.
Klantcase: Vastgoedplatform
Een vastgoedwebsite had 200.000 woningaanbiedingen. Gebruikers zochten op stad, prijsklasse, slaapkamers en woningtype. De zoekpagina had 4–6 seconden nodig om te laden omdat de query elke keer de hele tabel doorzocht.
Oplossing: Na analyse van de slow logs en gebruik van EXPLAIN voegden we een samengestelde index toe:
CREATE INDEX idx_city_price_beds_type ON properties (city_id, price, bedrooms, property_type);
We hebben ook de query gewijzigd om een coverende index te gebruiken door alleen de benodigde kolommen te selecteren (id, titel, prijs, miniatuur) in plaats van `SELECT *`.
Resultaten:
- Querytijd: 5,2 seconden → 90 ms.
- Server-CPU-belasting daalde met 70%.
- Pagin laadtijd: 6,5 s → 1,2 s (inclusief frontend).
- Gebruikersbetrokkenheid nam met 34% toe omdat gebruikers trage zoekopdrachten niet opgaven.
Veelgemaakte fouten en hoe u ze oplost
Fout 1: Elke kolom indexeren
Probleem: Te veel indexen vertragen schrijfbewerkingen. Oplossing: Indexeer alleen kolommen die voorkomen in WHERE, JOIN of ORDER BY.
Fout 2: Functies gebruiken op geïndexeerde kolommen
Slecht: WHERE DATE(created_at) = '2025-01-01' – negeert index op created_at. Goed: WHERE created_at BETWEEN '2025-01-01 00:00:00' AND '2025-01-01 23:59:59'.
Fout 3: Leidend jokerteken in LIKE
Slecht: WHERE name LIKE '%widget%' – gebruikt nooit een index. Goed: Gebruik een full-text zoekindex voor gedeeltelijke overeenkomsten.
Fout 4: EXPLAIN niet gebruiken voor en na
Voer altijd EXPLAIN uit voordat u een index toevoegt, en opnieuw erna, om de verbetering te verifiëren.
Geavanceerd: Indexsuggesties automatiseren
Voor MySQL 8.0 kunt u het performance schema inschakelen en het sys schema gebruiken om ontbrekende indexen te vinden:
SELECT * FROM sys.schema_unused_indexes;
SELECT * FROM sys.schema_redundant_indexes;
Voor oudere versies analyseert pt-index-usage van percona-toolkit de slow logs en stelt indexen voor.
Klaar om uw database te optimaliseren?
Ik bouw op maat gemaakte PHP-applicaties vanaf dag één met de juiste database-indexering. Of u nu een trage WordPress-site hebt of een aangepaste app met query-flessenhalsen, ik kan uw slow logs analyseren, de juiste indexen toevoegen en de laadtijden van pagina’s vaak met 50% of meer verminderen.
Laten we praten over uw databaseprestatieproblemen. Ik geef een gratis beoordeling van uw traagste query’s.
Alle prestatiecijfers zijn afkomstig van echte klantaudits met MySQL 8.0 op Hostinger VPS. Uw resultaten kunnen variëren op basis van tabelgrootte en serverhardware.