Sådan kodes server-side cache

Loadingtider mindskes, ressourceforbruget går ned og databaseserveren kan snuppe sig en lur, når man implementerer server-side cache til sit website. Med ganske få liniers kode øges effektiviteten med 400 procent i vores eksempel.

Applikationsvariabler

I den forrige artikel om server-side cache så vi på de forskellige muligheder, der er for at cache websiderne, og dermed minimere ressourceforbruget og nedsætte loadingtiderne. I denne artikel kigger vi på et konkret eksempel.

Som vi tidligere så, kan de spørgsmål, man skal afklare ved implementering, deles i to: Hvad skal man vælge at cache, og hvordan skal data gemmes. Det første spørgsmål besvarede vi ved at dele op i tre typer af caching: Cache af data fra datakilder, cache af enkelte dele af den producerede HTML-kode og cache af hele sider.

Data-caching
Data-caching kan benyttes til de tilfælde, hvor selve datafangsten kræver en større processering. Et typisk eksempel er komplekse SQL-sætninger eller blot dataudtræk, der kræver et stort antal SQL-forespørgsler. Et praktisk eksempel herpå kan være visninger af træstrukturer, såsom en trådet visning i et debatforum.

Det kan være, at de samme data vises forskelligt på de enkelte sider, og dermed kan behovet opstå for at gemme data i en struktur. Dette kræver en eller anden form for serialisering, som er den proces, hvor et objekt omskabes til sekventielle data. Både PHP, ASP og JSP har metoder til at serialisere objekter.

Element-caching
Hvis data derimod præsenteres på samme måde på alle siderne, giver element-caching endnu bedre ydelse. Her genereres dele af HTML-koden og placeres i cache. Hvis elementet benyttes ofte, som for eksempel en forside, så kan det bedst betale sig at placere elementet i serverens hukommelsesrum ved en applikationsvariabel. Man skal selvfølgelig ikke fylde hukommelsen med variable, så det er kun de allermest brugte elementer, man skal gemme på denne facon. Andre elementer kan man så gemme i en tabel i databasen.

Den sidste type af cache - nemlig at generere statiske HTML-sider via et publiceringssystem - ser vi bort fra i denne artikel, da denne situation ikke dækker over et egentligt dynamisk website.

Et eksempel

Versioner, triggers og opdatering
Cache er jo midlertidig lagring af indhold, og det er vigtigt at brugerne altid får den nyeste version af dette indhold. Med andre ord skal man programmere en mekanisme, som gør at cachen bliver opdateret, når indholdet ændres. Lad os se på et konkret eksempel, som kun berører cache-teknikker og ikke er optimeret på anden måde.

Lad os forestille os, at vi har en nyhedsportal, hvor en række artikler ligger i en tabel i en database. Vi genererer forsiden ved at udtrække hver artikels overskrift (som kaldes "rubrik" i jargonen), og udtrække artiklens oprettelsestidspunkt. Det kunne se sådan ud i ASP-dialekten JScript, Microsofts JavaScript-implementering:

<%@ Language=JScript %>

<html>
<body>

<%  

function genererForside()
  conn = Server.createobject("ADODB.Connection");
  conn.Open("website");
  artikler = conn.execute(
    "SELECT DATO, RUBRIK, ID " +
    "FROM ARTIKLER " +
    "ORDER BY DATO DESC"
  );
  while(!artikler.eof) {
    paenDato = new Date(artikler(0)+"");
    Response.write("<small class=\"dato\">"
      + paenDato.toLocaleString()
      + "</small>"
      + "\n"
      + "<h2 class=\"forside-rubrik\">"
      + "<a href=\"/vis_artikel.asp?ID="
      + artikler(2) + "\">"
      + artikler(1)
      + "</a>"
      + "</h2>"
      + "\n"
      + "<hr />"
      + "\n");
    artikler.movenext();
  }
  
  artikler.Close();
  conn.Close();
}

genererForside();

%>

</body>
</html>

Hvis siden har et stort antal eksponeringer, kan vi i stedet for at sende siden til browseren og gemme den genererede HTML-kode i en applikations-variabel.

Gemt HTML

Vi behøver kun ganske få omskrivninger af funktionen genererForsiden(), for at gemme den genererede HTML-kode i en applikationsvariabel:

function genererForsiden() {
  conn = Server.createobject("ADODB.Connection");
  conn.Open("website");
  artikler = conn.execute(
    "SELECT DATO, RUBRIK, ID " +
    "FROM ARTIKLER " +
    "ORDER BY DATO DESC"
  );

  var forside = "";

  while(!artikler.eof) {
    paenDato = new Date(artikler(0)+"");
    forside += "<small class=\"dato\">"
      + paenDato.toLocaleString()
      + "</small>"
      + "\n"
      + "<h2 class=\"forside-rubrik\">"
      + "<a href=\"/vis_artikel.asp?ID="
      + artikler(2) + "\">"
      + artikler(1)
      + "</a>"
      + "</h2>"
      + "\n"
      + "<hr />"
      + "\n";
    artikler.movenext();
  }
  
  Application("forsideKode") = forside;

  artikler.Close();
  conn.Close();
}

I stedet for at udskrive koden til browseren med Response.write(), opsamler vi koden i den lokale variabel forside, og gemmer til sidst koden i applikations-variablen forsideKode.

Opdatere hvornår

Når forsiden skal opdateres
Ideen er, at vi kun skal kalde funktionen genererKode, når det er absolut nødvendigt. Til at begynde med er variablen forsideKode tom, så vi skal i hvert fald starte med at gennemløbe funktionen. Dernæst skal vi opdatere forsideKode ved at kalde genererForside(), når forsiden skal opdateres, for eksempel når der bliver lagt en ny artikel ind i artikel-tabellen.

Det holder vi styr på ved en anden applikationsvariabel, som vi kan kalde opdaterForsiden. Når en ændring i systemet giver anledning til at opdatere, sættes variablen til sand (true). Efter at genererForside() er kaldt, sætter vi opdaterForsiden til falsk (false), da vi jo så netop har opdateret forsiden. Det kunne se sådan ud:

<%  

if( (!Application("forsideKode"))
    || Application("opdaterForsiden") )
{
  genererForsiden()
  Application("opdaterForsiden") = false;
}
Response.write(Application("forsideKode"));

function genererForsiden() {
  ...
  ...

%>

I den øverste if-sætning, spørger vi om forsideKode er defineret, eller om variablen opdaterForsiden er sand. I begge tilfælde skal vi kalde genererForsiden(), og det gøres i den næste linie. I den tredie linie sætter vi opdaterForsiden til falsk, og den næste linie - uden for if-sætningen - udskriver vi indholdet af forsideKode.

Vi har prøvet at teste, hvor stor forskellen var i udførelsen i forhold til de to situationer, hvor HTML-koden genereres, og hvor koden læses fra applikationsvariablen. Det gjorde vi ved at tilføje linien

starttid = new Date()

i toppen og i bunden af scriptet linierne:

sluttid = new Date();
Response.write("<h1>"
  + (sluttid.valueOf() - starttid.valueOf())
  + "</h1>");

Kort fortalt gemmer vi tidspunktet, hvor scriptet starter og tidspunktet hvor scriptet slutter, og udskriver tidsforskellen i millisekunder (sluttid.valueOf() - starttid.valueOf()). I dette konkrete tilfælde lå tidsforbruget, hvor forsiden genereres på omkring 200 millisekunder, mens situationen, hvor koden læses fra applikations-variablen, lå på omkring 50 millisekunder. Altså en reduktion til 25 procent, hvilket vel må siges at være ganske pænt i betragtning af, hvor få liniers kode der er til forskel i de to scripts.

Som det forhåbentlig ses, er det ikke så svært at implementere server-side cache. Eksemplet ovenfor er selvfølgelig en skitse, men princippet er ganske simpelt: Gem den genererede HTML-kode i en applikations-variabel, eller i en tabel i databasen, og hold styr på hvornår cachen skal genopfriskes.

Læses lige nu

    Event: Årets CIO 2026

    Andre events | København

    Vi samler Danmarks stærkeste digitale ledere til en dag med viden og visioner. Årets CIO 2026 fejrer 21 års jubilæum, og NEXT CIO sætter spotlight på næste generation. Deltag og bliv inspireret til at forme fremtidens strategi og eksekvering.

    4. juni 2026 | Gratis deltagelse

    Navnenyt fra it-Danmark

    Sourcing IT har pr. 2. februar 2026 ansat Susanne Sønderskov som Salgsdirektør. Hun skal især beskæftige sig med at styrke Sourcing IT’s kommercielle fundament, skalere salgsindsatsen og øge tilstedeværelsen bl.a. hos jyske kunder. Hun kommer fra en stilling som Salgsdirektør hos Right People Group ApS. Hun har tidligere beskæftiget sig med salgsledelse inden for IT-freelanceleverancer og komplekse kundeaftaler, både privat og offentligt. Nyt job

    Susanne Sønderskov

    Sourcing IT

    Idura har pr. 1. januar 2026 ansat Lars Mørch, 54 år,  som VP of Sales. Han skal især beskæftige sig med Iduras salgsorganisation, implementere en ny go-to-market-model og sikre udviklingen af virksomhedens identitetsplatform. Han kommer fra en stilling som Regional Vice President hos Avallone. Han er uddannet på CBS og har en BA i Organization & Innovation. Han har tidligere beskæftiget sig med internationalt SaaS-salg og forretningsudvikling fra både scale-ups og globale teknologivirksomheder. Nyt job

    Lars Mørch

    Idura

    Idura har pr. 15. januar 2026 ansat Mark-Oliver Junge, 26 år,  som software engineer. Han skal især beskæftige sig med at udvikle nye extensions, der gør godkendelsesprocesser mere fleksible, samt bygge infrastruktur til caller authentication. Han kommer fra en stilling som fullstack engineer hos Wayfare.ai. Han er uddannet Fachinformatiker für Anwendungsentwicklung, der betyder “ekspert i softwareudvikling”. Han har tidligere beskæftiget sig med udvikling af softwarearkitektur, DevOps og rammeværk til analyse + orkestrering af SQL-datapipelines. Nyt job
    Lector ApS har pr. 5. januar 2026 ansat Per Glentvor som Seniorkonsulent i LTS-gruppen. Per skal især beskæftige sig med med videreudvikling af Lectors løsning til automatisering og forenkling af toldprocesser. Per kommer fra en stilling som freelancekonsulent. Per har tidligere beskæftiget sig med løsninger indenfor trading, løsninger til detail, mobil samt logistik. Nyt job

    Per Glentvor

    Lector ApS