Week 5

Performantie

Deze week kwamen bijna uitsluitend optimalisatie en refactoring (en uiteraard de gewoonlijke bugfixes) aan bod. Ik begon deze week met het uitwerken van de tweede pagina van het prototype (waarop reizen getoond worden op basis van land en de zoekresultaten verfijnd kunnen worden). De eerste versie hiervan was zéér traag: vijf resultaten ophalen op basis van land kon zo’n vijf tot tien seconden in beslag nemen. De lezer vraagt zich nu misschien af “Hoe moeilijk kan het zijn om even alle reizen op te halen en even te vergelijken op de ID of de naam van het land?”. Op zich niet zo moeilijk, enkel dat de befaamde aliasmanager hierbij gemoeid is. Voor de lezers die de vorige posts niet lazen, de aliasmanager in het kort:

  1. Feeds bevatten producten.
  2. Producten bevatten properties.
  3. Properties hebben een naam en een waarde (bv. ‘country‘ is ‘Belgium‘).
  4. Aanbieders geven vaak andere namen aan ‘country’ (bv. ‘location’ of zelfs ‘region’, deze laatste bevat in andere feeds dan weer echt de regio, die we ook willen opslaan).
  5. Er is een lijst van ‘aliassen’ (bv. ‘country’, ‘region’, ‘city’, ‘startDate’, enzovoorts), m.a.w. een lijst van universele namen die de applicatie herkent.
  6. De aliasmanager laat toe per feed aan te geven welke propertynaam overeenkomt met welke alias.

Het land wordt hierdoor niet opgeslagen samen met het product zelf, maar wel in een aparte tabel properties met eigenschappen waarop verfijnd kan worden. Het wordt voor de lezer nu misschien duidelijker waarom het niet zo eenvoudig is de bijhorende landen voor de producten op te halen. Ik zal de SQL-query die hiervoor nodig is kort trachten te beschrijven.

  1. Voor alle producten, selecteer de properties (join) en de feed (join)
  2. Kijk welke aliassen horen bij de eigenschappen waarop de aanroepende functie wil verfijnen (bv. ‘country’ of ‘region’ en afhankelijk van de feed, dus nog een join ;)).
  3. Kijk welke propertynaam dit product gebruikt voor die alias en vergelijk de naam van elke property met deze naam alsook de bijhorende waarde (bv. ‘Duitsland’ voor ‘country’ en ‘Sauerland’ voor ‘region’).
  4. Addertje: alle properties zijn opgeslagen als tekst, maar bepaalde properties (zoals ‘startDate’) willen we kunnen vergelijken als datum, wat het moeilijk maakt om properties uniform te behandelen en we speciale code moeten voorzien voor bepaalde properties, wat de aanpasbaarheid en de flexibiliteit van het systeem vermindert.

Er kan daarnaast nog verfijnd worden op allerlei andere properties, zoals eigenschappen die bij het product zelf zitten. Hoe controleer je of een product aan alle criteria voldoet? Er moet voor dit product één property aanwezig zijn met de juiste waarde voor elk criterium dat op basis van aliassen geldt. Je kan hiervoor een WHERE EXISTS gebruiken met een subselectvoor elk criterium. Daarbij is de alias afhankelijk van de feed waardoor je aan het zoeken bent (en die hangt van het product af als je alle producten doorzoekt), dit maakt caching dus moeilijk voor SQL. Mijn idee was daarom om alle producten op te halen waarvan de properties aan één van de criteria voldoen, de properties te groeperen per product en dan via een HAVING COUNT(*) te controleren of aan alle criteria tegelijk voldaan werd. De performantie hiervan was een stuk beter, maar jammer genoeg volstond dit niet; de voorgenoemde resultaten bevatten deze optimalisatie al en bij het ophalen van zo’n 200 resultaten (het uiteindelijke doel) kon de tijd oplopen tot 5 minuten bij een zoekopdracht op basis van het land…

Meer performantie

Om de performantie verder omhoog te krikken, maakte ik deze week gebruik van twee ideeën, die ik elk apart zal beschrijven.

Het eerste idee bestond eruit een foreign key naar het locatiegegevens (land, regio en stad) bij het product zelf te plaatsen. Dit deed ik niet eerder omdat dit zou betekenen dat elke feed tweemaal ververst moet worden voordat alle gegevens opgehaald zijn; de eerste keer om te zien welke properties de producten hebben, waarna de administrator de aliassen instelt voor het land, de regio en de stad en de tweede keer om dan deze locatiegegevens op te halen en te plaatsen in de velden van het product zelf. De feedlezer kan tenslotte niet weten welke propertynaam het land bevat (hier heeft hij de aliassen en de aliasmanager voor nodig). Ik besloot deze optimalisatie ten slotte toch door te voeren omdat performantie in dit geval toch iets belangrijker is als gebruiksgemak voor de administrator.

Één van de aanvankelijke eisen van de feedlezer was dat alle informatie van de feeds (dus alle properties) bijgehouden dienen te worden, omdat ze in de toekomst eventueel nodig kunnen zijn. Totnogtoe stond al deze informatie dus in de database, wat kon oplopen tot zo’n twaalf miljoen records (zonder de producten waar ze aan vasthangen). Ik kwam echter op het idee om enkel die properties op te slaan die gemapt of gelinkt werden aan een alias; zo wordt de info op zich niet opgeslagen in de database, totdat iemand erom vraagt (en aangezien alle informatie toch via de aliasmanager opgehaald wordt, is dit geen probleem). Dit drong het aantal properties van twaalf miljoen terug naar zo’n 200.000 records; niet alleen vriendelijk voor de harde schijf, maar ook voor de performantie (minder rijen om te doorzoeken).

Samen betekenden deze twee aanpassingen een vrij forse sprong in performantie: waar het oorspronkelijk vijf tot tien seconden duurde om vijf resultaten op te halen, duurt het nu twee à drie seconden om 200 resultaten op te halen; een zeer goede vooruitgang! Echter is twee seconden nog vrij veel, aangezien nadien nog veel meer filters gebruikt zullen worden en de performantie verder achteruit zal gaan. Daarbij komt ook het probleem met mijn GROUP BY en HAVING-techniek die ik net beschreef aan het licht; omdat ik alle producten ophaal die aan één van de criteria voldoen, komen ook producten die niet aan alle criteria voldoen aan bood voor het groeperen en doorzoeken; SQL laat m.a.w. niet meteen deze producten vallen terwijl ze toch niet in aanmerking komen (tot bij het groeperen en de HAVING). Dit had met de WHERE EXISTS-techniek wel gekund, maar deze is in andere gevallen weer trager…

Concreet betekent dit dat volgende week opnieuw in het teken van performantie zal staan als het afwerken van het verfijnen van de zoekresultaten.

Reactie plaatsen

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *

*