Voordat we ingaan op voortijdige optimalisatie modus, kan het handig zijn om de volgende querysjabloon te bekijken. Als niets anders zou dit kunnen worden gebruikt als een basislijn waaraan de effectiviteit van mogelijke optimalisaties kan worden afgemeten.
SELECT T.Tagid, TagInfo.TagName, COUNT(*)
FROM Items I
JOIN Tags TagInfo ON TagInfo.TagId = T.TagId
JOIN ItemTagMap T ON I.ItemId = T.ItemId
--JOIN ItemTagMap T1 ON I.ItemId = T1.ItemId
WHERE I.ItemId IN
(
SELECT ItemId
FROM Items
WHERE -- Some typical initial search criteria
Title LIKE 'Bug Report%' -- Or some fulltext filter instead...
AND ItemDate > '02/22/2008'
AND Status = 'C'
)
--AND T1.TagId = 'MySql'
GROUP BY T.TagId, TagInfo.TagName
ORDER BY COUNT(*) DESC
De subquery is de "driving query", d.w.z. de query die overeenkomt met de initiële criteria van de eindgebruiker. (zie hieronder voor details over hoe deze query, die meerdere keren vereist is, kan passen in een algehele geoptimaliseerde stroom) Becommentarieerd is de JOIN op T1 (en mogelijk T2, T3, wanneer meerdere tags zijn geselecteerd), en, met de WHERE-clausule, de bijbehorende criteria. Deze zijn nodig wanneer de gebruiker een bepaalde tag selecteert, hetzij als onderdeel van de eerste zoekopdracht of door verfijning. (Het kan efficiënter zijn om deze joins en where-clausules in de subquery te plaatsen; meer hierover hieronder)
Discussie... De "driving query", of een variant daarvan, is nodig voor twee verschillende doeleinden:
-
1 om de volledige . te verstrekken lijst met ItemId die nodig is om alle bijbehorende tags op te sommen.
-
2 om de eerste N ItemId-waarden op te geven (N is de grootte van de weergavepagina), om de detailinformatie van het item op te zoeken in de itemtabel.
Merk op dat de volledige lijst niet gesorteerd hoeft te worden (of er baat bij kan hebben om in een andere volgorde te sorteren), waarbij de tweede lijst moet worden gesorteerd op basis van de keuze van de gebruiker (bijvoorbeeld op datum, aflopend of op titel, alfabetisch oplopend ). Houd er ook rekening mee dat als er een sorteervolgorde vereist is, de kosten van de query impliceren dat de volledige lijst moet worden behandeld (verlegen voor vreemde optimalisatie door SQL zelf en/of enige denormalisatie, SQL moet de laatste records op die lijst "zien" , voor het geval ze tot de top behoren, gesorteerd).
Dit laatste feit is in het voordeel van het hebben van dezelfde query voor beide doeleinden, de bijbehorende lijst kan worden opgeslagen in een tijdelijke tabel. De algemene gang van zaken zou zijn om snel de top N Item-records met hun details op te zoeken en deze in één keer terug te sturen naar de toepassing. De applicatie kan dan ajax-fashion de lijst met tags voor verfijningen verkrijgen. Deze lijst zou worden geproduceerd met een query die lijkt op die hierboven, waarbij de subquery wordt vervangen door een "select * from tijdelijke tabel". De kans is groot dat de SQL-optimizer besluit om deze lijst te sorteren (in sommige gevallen), laten we dat doen, in plaats van het te raden en het expliciet te sorteren.
Een ander punt om te overwegen is om de join(s) op de ItemTagMap-tabel in de "driving-query" te plaatsen in plaats van zoals hierboven weergegeven. Het is waarschijnlijk het beste om dit te doen, zowel voor de prestaties als omdat het de juiste lijst zal produceren voor doel #2 (weergave van een pagina met items).
De hierboven beschreven query/stroom zal waarschijnlijk redelijk goed schalen, zelfs op relatief bescheiden hardware; voorlopig in de 1/2 miljoen+ items, met aanhoudende zoekopdrachten van gebruikers misschien tot 10 per seconde. Een van de belangrijkste factoren is de selectiviteit van de initiële zoekcriteria.
Optimalisatie-ideeën
- [Afhankelijk van de typische zoekgevallen en van de gegevensstatistieken] kan het zinvol zijn om te denormaliseren door enkele velden van ItemTagMap naar de ItemTagMap-tabel te brengen (inderdaad dupliceren). Vooral korte velden kunnen daar 'welkom' zijn.
- Naarmate de gegevens met meer dan een miljoen items groeien, kunnen we gebruikmaken van de typisch sterke correlatie van sommige tags (bijvoorbeeld:in SO wordt PHP vaak geleverd met MySql, trouwens vaak zonder goede reden...), met verschillende trucs. De introductie van "multi-Tag" TagIds zou bijvoorbeeld de invoerlogica wat gecompliceerder kunnen maken, maar zou ook de kaartgrootte aanzienlijk kunnen verkleinen.
-- 'nou gezegd! --
De juiste architectuur en optimalisaties moeten worden gekozen in het licht van de werkelijke vereisten en het effectieve statistische gegevensprofiel...