Ik heb twee redelijk grote tabellen:
Tabel A (honderdduizenden rows):
Tabel B (miljoenen rows):
Eigenlijk heb ik nog veel meer tabellen, maar dit is de essentie van het probleem. Ik ben bezig met een zoekmachine die ik een uniforme query wil laten genereren die MySQL aan de hand van de indices en hun cardinaliteit zal gaan moeten optimaliseren. Probleem: MySQL faalt af en toe in het optimaliseren waardoor ik genoodzaakt wordt om heel lelijke queries te schrijven om het toch nog snel te krijgen. Vanzelfsprekend laat ik regelmatig de tabellen analyzeren om de verspreiding van de indices bij te werken.
Eerste voorbeeld waarbij het foutgaat:
SELECT * FROM a INNER JOIN b ON a.b_id = b.id ORDER BY b.value ASC LIMIT 0, 30;
De snelle manier: sorteer tabel b aan de hand van de index op value, en zoek vervolgens de juiste records terug uit tabel a en elimineer alle rows uit b die niet terug te vinden zijn totdat je 30 records hebt.
MySQL bedacht echter dit: maak een temporary table met alle records uit tabel a gejoined met b, sorteer deze vervolgens en pak de eerste 30 records. De enige manier waarop ik MySQL kan forceren dit gedrag te vermijden is dit:
SELECT STRAIGHT_JOIN * FROM b INNER JOIN a ON b.id = a.b_id ORDER BY b.value ASC LIMIT 0, 30;
Tweede voorbeeld waarbij het foutgaat:
SELECT * FROM a
INNER JOIN b b1 ON a.b_id = b1.id
INNER JOIN b b2 ON a.b_id = b2.id
WHERE b1.value = constante1 AND b2.value = constante2
De snelle manier: pak alle records uit tabel b waar value gelijk is aan constante1 en vervolgens aan constante2. Dit gaat snel vanwege de index. Laat deze gegevens intersecten op basis van b.id en neem alle waardes voor b.id waar ze in allebei de recordsets voorkomen. Join vervolgens tabel a op deze waardes door middel van a.b_id, wat ook snel gaat vanwege de index.
MySQL doet echter: 47 seconden over deze join? Terwijl ik zelf, met bovenstaande methode, véél sneller resultaat heb.
Waarom doe je het dan niet op de 'snelle manieren' zoals je boven beschreven hebt?
Dit zijn maar twee voorbeeldjes waar in werkelijkheid een legio van dit soort problemen opduikt. Ik wil op de één of andere manier MySQL duidelijk maken hoe het moet, zonder voor elk specifiek probleem een aparte query te schrijven. Maar hoe?
Tabel A (honderdduizenden rows):
| id | UNSIGNED MEDIUMINT | PRIMARY |
| b_id | UNSIGNED INT | INDEX, FK -> b.id |
Tabel B (miljoenen rows):
| id | UNSIGNED INT | INDEX |
| value | UNSIGNED INT | INDEX |
Eigenlijk heb ik nog veel meer tabellen, maar dit is de essentie van het probleem. Ik ben bezig met een zoekmachine die ik een uniforme query wil laten genereren die MySQL aan de hand van de indices en hun cardinaliteit zal gaan moeten optimaliseren. Probleem: MySQL faalt af en toe in het optimaliseren waardoor ik genoodzaakt wordt om heel lelijke queries te schrijven om het toch nog snel te krijgen. Vanzelfsprekend laat ik regelmatig de tabellen analyzeren om de verspreiding van de indices bij te werken.
Eerste voorbeeld waarbij het foutgaat:
SELECT * FROM a INNER JOIN b ON a.b_id = b.id ORDER BY b.value ASC LIMIT 0, 30;
De snelle manier: sorteer tabel b aan de hand van de index op value, en zoek vervolgens de juiste records terug uit tabel a en elimineer alle rows uit b die niet terug te vinden zijn totdat je 30 records hebt.
MySQL bedacht echter dit: maak een temporary table met alle records uit tabel a gejoined met b, sorteer deze vervolgens en pak de eerste 30 records. De enige manier waarop ik MySQL kan forceren dit gedrag te vermijden is dit:
SELECT STRAIGHT_JOIN * FROM b INNER JOIN a ON b.id = a.b_id ORDER BY b.value ASC LIMIT 0, 30;
Tweede voorbeeld waarbij het foutgaat:
SELECT * FROM a
INNER JOIN b b1 ON a.b_id = b1.id
INNER JOIN b b2 ON a.b_id = b2.id
WHERE b1.value = constante1 AND b2.value = constante2
De snelle manier: pak alle records uit tabel b waar value gelijk is aan constante1 en vervolgens aan constante2. Dit gaat snel vanwege de index. Laat deze gegevens intersecten op basis van b.id en neem alle waardes voor b.id waar ze in allebei de recordsets voorkomen. Join vervolgens tabel a op deze waardes door middel van a.b_id, wat ook snel gaat vanwege de index.
MySQL doet echter: 47 seconden over deze join? Terwijl ik zelf, met bovenstaande methode, véél sneller resultaat heb.
Waarom doe je het dan niet op de 'snelle manieren' zoals je boven beschreven hebt?
Dit zijn maar twee voorbeeldjes waar in werkelijkheid een legio van dit soort problemen opduikt. Ik wil op de één of andere manier MySQL duidelijk maken hoe het moet, zonder voor elk specifiek probleem een aparte query te schrijven. Maar hoe?