Vi ansetter!

Hvordan fungerer caching i WordPress?


Artikkel av Thomas Audunhus. Published on februar 14, 2018

Caching. Det kan virke som at det er «the holy grail» for alle performance problemer. Da er det ikke rart at folk hever øyebrynene når jeg sier «Slutt å bruke caching» i mine foredrag, meetups eller workshops. For enkelte, spesielt i WordPress-miljøet har jeg blitt han som hater caching. Så, nå er det på tide med en oppklaring i hva jeg synes om caching, når jeg mener det skal brukes, og hva det skal brukes til. Og kanskje viktigst av alt, når man ikke skal bruke caching og heller se mot andre løsninger.

Når skal du bruke full page caching?

La oss starte med den største. Full page caching, eller page caching. En mekanisme hvor man midlertidig lagrer en ferdig generert side på server for å kunne levere nøyaktig samme kode (HTML) til besøkende innenfor et begrenset tidsrom. Slik fungerer Full Page Caching:

  1. Visitor A besøker abc.no/side. Denne siden ligger ikke i cache, og treffer derfor PHP og blir generert fra databasen. Før den serveres til Visitor A blir den også lagret i cache med en utløpstid på 10 minutter.
  2. Visitor B besøker abc.no/side 2 minutter etter Visitor A, og får derfor servert den samme siden, men nå fra cache. Siden som blir servert til Visitor B og A er helt identiske.
  3. Visitor C besøker abc.no/side 15 minutter etter Visitor A, og ettersom den cachede versjonen av siden er utløpt – treffer trafikken PHP og siden blir på nytt lagret i cache.

Siden alle som besøker samme siden i tidsrommet hvor det finnes gyldig cache får servert versjonen som ligger i cache, kan siden serveres lynraskt. Det brukes forsvinnende lite ressurser til å servere siden fra cache, og derfor kan full page caching hjelpe på performance, og på skalering. Det høres forlokkende ut – men full page caching har også noen store ulemper.

  1. Hvis du skal levere dynamisk, personalisert eller på annen måte forskjellig innhold til forskjellige brukere må dette løses med feks javascript (ajax) som kjøres etter at html-dokumentet er levert til browser. I mange tilfeller går det helt fint, men ved bruk av ajax vil du likevel lage requests til serveren. Så dersom du bruker caching for å slippe unna å skrive kode som er rask vil du allikevel få et problem hvis du skal levere dynamisk innhold.
  2. Selv om du vil føle siden som rask betyr det ikke at alle vil gjøre det. Nettsider i dag er ikke «single point of entry», og folk lander på veldig mange forskjellige og tilfeldige sider. Dette gjelder spesielt for nettbutikker hvor folk Googler seg rett til produktsider, eller hvis man kjører annonser i f.eks Google Shopping på hele produktkatalogen med lenker rett til produktet. For å angripe dette finnes det folk som forsøker seg med «priming» av full page cache, altså bruker feks en spider som crawler hele nettsteder sånn at alt til en hver tid skal være cachet. Dette fungerer dog sjeldent, er sårbart, vanskelig å sette opp ordentlig, tar enormt med tid å vedlikeholde, og er ikke minst helt unødvendig.
  3. Du vil ha mindre følelse av den faktiske hastigheten, og hvordan koden fungerer. Bruker du full page cache mister du muligheten i mange tilfeller til å se på responstiden hvordan status er, hvor god koden din er osv. All erfaring viser også at mangelen på fokus på den egentlige hastigheten er  hovedårsaken til at siter kræsjer på Black Friday og ved utsendelse av store kampanjer.

Hva med Caching plugins som W3 Total Cache?

WordPress Plugins som W3 Total Cache fungerer i bunn og grunn som all annen page caching. De lagrer en versjon av en ferdig generert side til disk(fil) eller minne og serverer den til brukerne frem til utløpstiden er nådd. Selv om W3 Total Cache er brukt av over 1 million sider betyr ikke det at det er en god idé, og spesielt ikke om du kjører rask hosting. W3 Total Cache er en enorm plugin, og med mindre du ikke har noe annet valg er det enormt mye unødvendig kode. Mer kode, betyr mer som kan gå galt. Om du absolutt må kjøre page caching så burde ikke dette gjøres i PHP, for PHP er veldig tregt. Det burde gjøres før trafikken treffer PHP(Apache). Og, har du konfiguerert W3 Total Cache feil er det en falsk trygghet.

W3 Total Cache snakker om å få sider til å laste under 2 sekunder, og bruker en standard site med twentysixteen som eksempel. Bare for å ha sagt det, en likende test hos oss laster på millisekunder, ikke sekunder.

Så når skal du egentlig bruke Full Page Caching, og hvorfor?

Full page caching ble laget for å enklere skalere nettsider med mye trafikk, og enklere håndtere spikes i trafikken til noen sider. Full Page Caching stammer fra tiden hvor folk brukte internett nesten utelukkende til å lese nyheter og se oppdatert værmelding, og avisene hadde et enormt trykk til noen få artikler (les. toppsakene). Å generere all HTML for hver enkelt besøkende da er helt unødvendig, og sidene endret seg sjeldent. For dette scenarioet skal du fortsatt bruke Full Page Caching. Men, nå lager vi nettsider som likner mer på applikasjoner.

Nettsider i dag er mer komplekse, mer dynamiske, og litt etter litt også mer personaliserte. Koden har generelt blitt vesentlig mer kompleks, og å legge på flere komplekse mekanismer (les ulike cachingmekanismer) på toppen av kompleks kode er ikke alltid en god idé. Det man i praksis gjør er å legge på enda et lag med teknologi som skal vedlikeholdes, som kan skape problemer for utvikler, og som legger til et unødig single point of failure. Koder du en løsning hvor du baserer ytelse på Full Page Caching kan jeg nesten garantere at løsningen vil klappe sammen fullstendig dersom cachingen skulle slutte å fungere.

Svaret på spørsmålet over er derfor; skriv god kode og ha i bakhodet at koden skal kunne skaleres, og bruk eventuelt Full Page Caching til å ta unna toppene av trafikken. Akkurat som avisene gjorde før, og forsåvidt gjør fortsatt.

Du skal kunne skru av Full Page caching på en vanlig dag, med vanlig trafikk, uten å være nærvøs. Og er du ordentlig god, og gjør jobben din, skal du kunne skru av full page caching på en dag med mye trafikk uten at løsningen kollapser.

På Servebolt kjører den store majoriteten av nettbutikker uten Full Page Caching selv på høytrafikkdager som Black Friday.

 

Object cache – du bruker den uten å vite det

Veldig mange vet ikke at Object Cache er i bruk på så og si alle WordPress installasjoner. Og ja, du bruker Object Cache selv om du ikke har ekstra programvare som MemCached, REDIS eller liknende installert.

Slik fungerer WordPress Object Cache

  1. Du gjør en spørring etter feks postmeta verdier ved å bruke get_post_meta()
  2. WordPress kjører spørringen og får et resultat fra databasen, og kjører resultatet inn i Object Cachen (minnet, RAM)
  3. Du får et resultat som du bruker et eller annet sted i koden din
  4. Lenger ned i koden spør du på nytt om de samme dataene
  5. WordPress vet resultatet av spørringen du nå gjør for andre gang ligger klart i Object Cachen og leverer resultatet lynraskt
  6. Siden er ferdig lastet, og Object Cache tømmes (minnet frigjøres igjen)

Det er ikke alle funksjoner i WordPress som lagrer resultatene i Object Cache, men get_post_meta() gjør det. Det er fordi _postmeta tabellene i databasen fort kan bli enorme, og spørringene kan derfor bli veldig kostbare (kreve mye datakraft, ta lang tid etc).

Som utvikler kan selv legge resultater inn i object cache slik at du raskere kan bruke resultatene senere på samme sidevisning. Om du skriver dine egne spørringer med feks WP_Query må du legge til resultatene i object cache på egenhånd. Men det er viktig å huske, Object Cache er som all annen cache. Du kan aldri stole på den, og derfor burde du heller ikke basere koden din på at Object Cache eksisterer.

Du kan sjekke hvor mange verdier som blir hentet fra Object Cache og hvor mange som hentes fra databasen ved å installere pluginen Query Monitor. I mine tester har en vanlig WooCommerce butikk normalt en hitrate på mellom 95% og 98%.

Slik bruker du Object Cache på egne spørringer

$result = wp_cache_get( 'some_unique_name' ); 

if ( false === $result ) { 
   $result = $wpdb->get_results( $query );
   wp_cache_set( 'some_unique_name', $result );
}
// Do something with $result;

Det dette kodeeksempelet gjør er å først sette $result, og så sjekke om $result finnes. wp_cache_get() returnerer false om den ikke eksisterer. Så om den ikke eksisterer kjører vi spørringen som trengs for å få tak i dataene vi skal bruke. Deretter setter vi cache slik at vi kan bruke resultatet igjen raskt.

Men hva med ekstern Object Cache som Memcached eller REDIS?

Det en ekstern cache kan gjøre er å gi deg en «persistent object cache». Altså en Object Cache som ikke blir frigjort når en side er ferdig generert, men som kan forbli der slik at resultatet av en spørring kan brukes på kryss og tvers av sidevisninger. Fantastisk idé, ikke sant? Men som vanlig er ikke alt så rosenrødt når det gjelder caching, heller ikke her.

  1. Potensialet til ytelsesforbedringer er begrenset til den andelen som ikke allerede treffer Object Cachen. Altså maksimalt et sted mellom 2% og 5% av spørringene til Object Cache. I tillegg vil en Object Cache legge til noe treghet fordi det er en ekstern applikasjon. Erfaring viser at summen av treghet + gevinst fra ekstern Object Cache ofte gir et negativt nettoresultat for nettbutikker.
  2. Selv om du bruker en ekstern Object Cache så kan du ikke stole noe mer på at cachen faktisk eksisterer når du skriver din kode
  3. Om du er avhengig av en ekstern Object Cache for å få sidene til å laste i grei hastighet så betyr det at koden din er dårlig eller at databaserspørringene er for trege. Problemet ditt ligger i koden, ikke i hvor raskt databasen svarer. Og derfor burde du ikke sette på «plasteret» MemCached eller Redis.
  4. Bruker du en rask database som er optimalisert og har god bruk av indekser har du ikke bruk for en ekstern Object Cache.

Har du ikke bruk for ekstern Object Cache kan det være direkte skadelig å bruke det

Skadelig er kanskje å dra det litt langt, men i ytterste konsekvens kan det være det. Det gjelder egentlig all teknologi du har i «stacken» som du ikke trenger. Har du det er sjansen stor for at du gjør deg avhengig av det, selv om du ikke hadde trengt det. Er du avhengig av det så har du økt sjansen for at noe går galt. I tillegg er det mer å vedlikeholde, konfigurere på riktig måte, og det blir en ekstra kilde til nye feil og problemer.

Transients kan redde, men også ta knekken på siden din

Transients brukes av WordPress og en hel rekke plugins, og konseptet er ganske enkelt. Du gjør en spørring, og lagrer resultatet, eller deler av det, i databasen for å enkelt kunne bruke resultatet senere. Til forskjell fra Object Cache så trenger du ingen ekstra teknologi for å bruke transients på tvers av sidevisninger. Transients kan ha en utløpstid, eller ikke. Det velger du.

Personlig liker jeg ikke overdreven bruk av transients, og grunnene er mange

  1. Transients blir lagret i _options tabellen. En tabell som allerede har mye trafikk. Skriving til _options i stor stil kan skape locking problematikk (kø).
  2. Overdreven bruk av transients vil resultere i en stor _options tabell. Absolutt alle sider er avhengig av denne tabellen, og en stor options tabell kan også bidra til dårligere performance på alle sidevisninger.
  3. Transients som ikke utløper vil automatisk bli lastet når WordPress laster alle options gjennom wp_load_alloptions(). Dette vil også resultere i dårligere performance på alle sidevisninger.

Men, når det er sagt så er riktig bruk av transients veldig bra for performance. Om du har en tung spørring som kjøres ofte og sjeldent endrer seg så er det en veldig god plan å cache resultatet i en transient. Det er mye lettere for WordPress å hente verdien fra key X i _options tabellen enn å kjøre selects e.l fra stort sett alle andre tabeller. Men, etter du har tatt i bruk transients ville jeg holdt et godt øye med _options tabellen for å sikre at den ikke vokser alt for mye, og husk å implementere logikk for å slette transients du ikke lenger trenger, bruker eller som har utløpt. Og ikke bruk transients for data du trenger skal være løpende oppdatert.

Fragment Cache gir resultater, om du bruker det riktig

Fragment cache er en type caching hvor du lagrer et element, del av en side, eller noe annet som rent ressursmessig er dyrt å generere og/eller brukes ofte. Mange har valgt å implementere en versjon av Mark Jaquith sin funksjon for å gjøre Fragment caching i WordPress. Fragment Cache betyr at man lagrer resultatet (HTML output), slik at det er klart til å leveres lynraskt neste gang. Ideen bak Fragment Caching er jo like enkel som den er fantastisk. Du kan selv velge hvilke elementer som skal caches, og du trenger ikke gjøre som med Full Page Caching å lagre hele sider. Måten du gjør det på i WordPress er samme måte som Object Caching, ettersom det ikke er noen begrensning på hva slags data du kan cache. Og derfor gjelder de samme prinsippene; du kan ikke stole på at cachen eksisterer, ikke gjør deg avhengig av fragment cache og fokuser på koden fremfor å ta snarveien innom fragment cache.

Mange, sogner til en løsning hvor man gjør fragment caching i minnet. Men, for at dette skal ha effekt må man ha en ektern Object Cache for å beholde Cachen etter at siden er ferdig lastet, noe jeg ikke anbefaler (les over). Jeg anbefaler heller forsiktig bruk av Fragment Cache, og å lagre data i transients. Funksjonelt er det helt likt som minnet, det er bittelitt tregere, men du trenger ikke legge på ny teknologi som betyr en stødigere løsning.

Slik setter du transients enkelt

$output = get_transient('some_unique_key');

if( $output === false ){

$output = 'Some data';
set_transient('some_unique_key', $output, 3600);

}
// Do something with $output

Det dette kodeeksempelet gjør er å først sette $output, og så sjekke om $output finnes. get_transient() returnerer false om den ikke eksisterer. Så om den ikke eksisterer kjører vi spørringen som trengs for å få tak i dataene vi skal bruke. Deretter setter vi en transient slik at vi kan bruke transienten for å hente dataene raskt.

Hvilken cache skal du bruke når?

Bruk Full Page Caching til skalering – for å ta av for de største trafikktoppene, men ikke gjør deg avhengig av den. Du skal kunne skru av full page cache uten at applikasjonen detter ned under vanlig belastning.

Bruk Object Cachen flittig, spesielt om du bruker WooCommerce. Men hold deg unna eksterne Object Cacher. Det kommer til å bli mer jobb enn nødvendig og gi ta fokus bort fra koden din. Skaff deg heller en raskere host med lynraske databaser, som vi har.

Bruk Transients til spørringer du gjør ofte men som sjeldent endrer seg. Husk å sette en fornuftig utløpstid basert på hvor ofte verdien blir oppdatert. Husk også å slette utløpte Transients. Bruk fragment cache i transients til elementer som er tunge å lage, og som skal brukes ofte.

More about Thomas Audunhus

Thomas leder satsningen i vårt hjemland, Norge, og er ekspert på WordPress og WooCommerce. Thomas har bygget noen av de raskeste WordPress og WooCommerce sidene i Norge, og vet derfor nøyaktig hvordan man skal få WordPress og WooCommerce så rask som mulig. I tillegg til å arbeide hos oss er Thomas sentral i det norske WordPress og WooCommerce miljøet. Han er en av organisatorene av Oslo WordPress meetup, og ikke minst lead organizer av WordCamp Oslo 2018.