BLOG

Expert column

AmsterdamPHP: Demystifying cache in Doctrine ORM

27 september 2017

Onze collega Frank bezocht de laatste bijeenkomst van de meetup groep AmsterdamPHP. Hier sprak Luís Cobucci, PHP ontwikkelaar en Doctrine core developer over caching in Doctrine.

De trade-off van een ORM

Doctrine is een ORM voor het PHP platform. Een ORM is een software component welke ervoor zorgt dat een programmeur vanuit zijn applicatie kan communiceren met een database, zonder zich bezig te houden met details zoals de verbinding naar de database, de specifieke taal en syntaxis die er gesproken wordt met de database enzovoort. Sterker nog: door een ORM te gebruiken is een stukje data uit een database hetzelfde als een stukje data binnen de applicatie zelf. Doctrine verwijdert technische details, maakt een abstractie van de werkelijkheid en maakt daarmee de software kleiner en makkelijker leesbaar, met als einddoel een betere maintainability.

Maar zoals zo vaak met abstracties is er een trade-off. Software gemaakt met een ORM is dan wel significant beter te onderhouden, we leveren altijd een stuk in op performance en schaalbaarheid. Door aan alle knoppen zelf te kunnen draaien is een betere performance te behalen dan wanneer de ORM deze knoppen voor ons verbergt.
Doctrine heeft een effectief mechanisme om deze trade-off tot het minimum te brengen. Door intelligente caching toe te passen is Doctrine ook zeer geschikt geworden voor high-performance applicaties. Maar wat is caching, en hoe werkt het?

‘Niet naar de bekende weg vragen’

Cachen betekent eigenlijk ‘niet naar de bekende weg vragen’. Wanneer een applicatie een moeilijke berekening of een andere intensieve actie moet uitvoeren, en we weten dat de applicatie enige momenten later precies dezelfde actie moet uitvoeren, dan is het slim om de uitkomst te bewaren voor dit volgende moment. Dit is precies wat we doen bij cachen. Doctrine heeft vier verschillende plaatsen waar het gegevens cacht om te hergebruiken wanneer de applicatie deze gegevens weer nodig heeft (i.e. naar de bekende weg vraagt).

Caching lagen in Doctrine

De eerste cache laag is metadata cache. Metadata is een heel breed begrip, maar binnen Doctrine wordt hier gesproken over ‘achtergrondinformatie’ over de data die in een applicatie een rol speelt. Stel je voor dat een programmeur bij Doctrine een ‘Land’ opvraagt, dan moet Doctrine weten welke eigenschappen er allemaal in de database staan voor landen (namen, valuta’s, talen?),  welke relaties er zijn met andere soorten data, en hoe dit alles opgehaald moet worden. Deze gegevens moeten geëxtraheerd worden uit configuratiebestanden. Maar waarom zouden we dit opnieuw doen, als deze configuratiebestanden onveranderd zijn gebleven? Doctrine cacht metadata zolang de configuratie van de gegevens die hij verwerkt niet aangepast is.

De tweede cache laag is de query cache. Deze is voor iemand die Doctrine niet intensief begrijpt misschien wat minder makkelijk te begrijpen, maar erg belangrijk. Zoals ik in de inleiding vertelde abstraheert Doctrine de details van een database om het leven van een programmeur makkelijker te maken. Eén van de manieren waarop Doctrine dat doet is met DQL: de Doctrine Query Language. Het is een eigen taal waarmee gegevens kunnen worden opgevraagd uit een database, zonder je druk te maken om de specifieke (Structured) Query Language die de database spreekt. Doctrine zet de DQL die je schrijft automatisch om in de taal die de database op de achtergrond spreekt. En in deze omzetting zit de query cache. Want wanneer twee keer dezelfde informatie gevraagd wordt aan dezelfde database (dat wil zeggen: er twee keer dezelfde DQL vraag wordt gesteld), de vraag in de taal van de database, (S)QL, ook gelijk is. In plaats van het opnieuw vertalen van de DQL kan dan gewoon weer dezelfde vraag in (S)QL gebruikt worden.

De derde cache laag, en de laatste die de meeste Doctrine developers kennen, is de result cache. De result cache is anders dan de vorige twee soorten cache, omdat deze expliciet door de programmeur aangezet moet worden. De result cache brengt namelijk een geheel eigen trade-off met zich mee, en de beslissing of deze trade-off de moeite waard is kan alleen door de programmeur van de applicatie genomen worden.

Bij de result cache kan het resultaat van een vraag aan de database voor een aangegeven aantal seconden worden opgeslagen. Dus in plaats van dat Doctrine de vraag opnieuw aan de database stelt wanneer het opnieuw gevraagd wordt, geeft hij voor een bepaalde tijd gewoon het vorige antwoord terug. Het antwoord is dus niet altijd correct (de database kan inmiddels een ander antwoord geven op de vraag), maar wel snel. De trade-off zit hier dus tussen accuratie en performance.

Een voorbeeld waarbij een result cache heel nuttig is: een webshop die op de homepage aangeeft hoeveel bestellingen er al geplaatst zijn. Performance is hier heel belangrijk: bij een webshop wil de klant echt niet wachten op het laden van de pagina. Accuratie is minder belangrijk. Of het aantal bestellingen nu 1 000 000 of 1 000 010 is maakt voor de klant die de webshop bezoekt een klein verschil. Het gebruiken van de result cache is hier een heel goed idee.

Doctrine second level cache

Luís introduceerde ook de second level cache van Doctrine. Een caching laag in Doctrine die nieuw is in Doctrine 2.5, stabiel zal zijn in 2.6 en van grote betekenis zal zijn voor de Doctrine-wereld. Second level caching is bijna een ‘result cache 2.0’ te noemen. In plaats van het cachen van het resultaat van één vraag wordt het mogelijk om een hele groep informatie in cache te houden. Zo kunnen vragen (queries) op bepaalde soorten informatie sneller verwerkt worden. Hier kleeft wel de trade-off aan vast dat de resultaten op deze query’s niet gegarandeerd hetzelfde zijn als dat ze in de database zouden zijn. Maar ze zijn wél snel. Second level cache is bijvoorbeeld heel nuttig om te gebruiken bij data waarvan je als programmeur weet dat de gegevens nooit of bijna nooit veranderen. Bijvoorbeeld een tabel met alle landen zal niet snel veranderen, of een tabel met de Nederlandse BTW-schalen.

Veranderen de gegevens toch een keer? Dan is het altijd mogelijk om de cache te legen en opnieuw te vullen.

Meer weten over de Doctrine Second Level Cache? Kijk dan in de Doctrine documentatie, of lees de slides hierboven nog een keer terug!

Interesse in een gesprek?

neem contact op met Geurt Jan van Ek

Neem contact op

Zie onze privacyverklaring.

Contact met Senet

Senet Eindhoven
Gestelsestraat 258
5654 AM Eindhoven
Bekijk op kaart

+31(0)40-2930395

KvK nummer: 17115078
Btw nummer: NL807989083B01