Multi-tenant arkitektur forklaret: én database eller mange?
Multi-tenant arkitektur i SaaS: delt database, schema pr. kunde eller database pr. kunde. Fordele, ulemper og hvornår jeg vælger hvilken model.

Freelance full-stack udvikler
- Udgivet
- Læsetid
- 12 min.
Indhold i indlægget9
Multi-tenant arkitektur betyder at én installation af din SaaS betjener mange kunder på én gang, mens hver kundes data holdes adskilt fra de andres. Der findes tre grundmodeller: én delt database hvor hver række er mærket med et kunde-id, ét schema pr. kunde, eller én database pr. kunde. Til langt de fleste nye SaaS-produkter anbefaler jeg den delte database, og jeg flytter kun en kunde over i sin egen database når et konkret krav fra kunden gør det besværet værd.
Valget er dyrt at lave om senere. Derfor er det en af de få tekniske beslutninger du bør forstå som ejer af produktet, også selvom du ikke selv skriver koden.
Den korte version
| Delt database | Schema pr. kunde | Database pr. kunde | |
|---|---|---|---|
| Sådan adskilles data | Et kunde-id på hver række | Egne tabeller i et separat schema | En hel database pr. kunde |
| Adskillelse | Lav: koden skal filtrere korrekt hver gang | Middel | Høj |
| Driftsomkostning | Lavest | Lav til middel | Højest |
| Migreringer | Én gang | Én gang pr. kunde | Én gang pr. kunde |
| Gendan én kunde fra backup | Besværligt | Muligt | Nemt |
| Tilpasning pr. kunde | Svært | Muligt | Muligt |
| Passer typisk til | De fleste nye SaaS-produkter | PostgreSQL-projekter med et moderat antal kunder | Store kunder med krav om adskilte data |
Min tommelfingerregel er enkel. Vælg en delt database, medmindre en kunde, en kontrakt eller en lov allerede i dag kræver fysisk adskillelse. Gør de det, får netop de kunder deres egen database, mens resten bliver i den delte.
Tenancy er én af flere arkitekturbeslutninger i et SaaS-projekt. Overblikket over resten finder du i min guide til at bygge en SaaS fra idé til betalende kunder.
Hvad en tenant er, og hvad den ikke er
En tenant (på dansk nærmest "lejer") er den enhed der betaler for og ejer data i dit system. I B2B-SaaS er det næsten altid en virksomhed, en organisation eller et team, ikke den enkelte bruger. Et værktøj til revisorer har fx revisionsfirmaer som tenants og mange brugere fordelt på hvert firma.
Forskellen afgør hvor grænsen skal gå. Brugere i samme virksomhed må gerne se hinandens data, alt efter hvilke roller de har. Brugere fra to forskellige virksomheder må aldrig. Roller styrer det første, og det har jeg skrevet om i indlægget om brugerroller og rettigheder i SaaS. Tenancy styrer det sidste.
Det modsatte af multi-tenant er single-tenant: hver kunde får sin egen installation af hele applikationen, ofte på sin egen server. Det giver maksimal adskillelse, men hver opdatering skal rulles ud til hver kunde for sig. Det er sjældent det rigtige til et standardprodukt, men det har sin plads, som du kan læse længere nede.
Bemærk også at de tre modeller handler om databasen. Koden er den samme for alle kunder i alle tre.
Model 1: én delt database med kunde-id
Alle kunder deler de samme tabeller, og hver række har en kolonne, typisk tenant_id, der fortæller hvem den tilhører. Når en bruger logger ind, ved applikationen hvilken kunde brugeren hører til, og alle forespørgsler bliver filtreret på det id.
Fordelene er meget praktiske. Du har én database at tage backup af, overvåge og opgradere. En migrering (en ændring af databasens struktur) køres én gang. Statistik på tværs af kunder, fx hvor mange der bruger en ny funktion, er en almindelig forespørgsel. Og en ny kunde koster næsten ingenting at oprette, for det er bare en ny række i en tabel.
Ulempen er at adskillelsen kun er så god som koden. Glemmer en udvikler filteret ét sted, kan kunde A se kunde B's fakturaer. Microsoft siger det ret direkte i sin gennemgang af tenancy-modeller til SaaS: en database med mange kunder ofrer noget adskillelse. Der er også risiko for en "støjende nabo", hvor én stor kunde med tunge rapporter gør systemet langsomt for alle andre.
Sådan undgår du at data lækker mellem kunder
I Laravel er den naturlige løsning at lægge filteret ind som et global scope, altså et filter der automatisk bliver tilføjet alle forespørgsler mod en model. Så er det ikke op til den enkelte udvikler at huske det. Et global scope dækker dog ikke alt, så det er disse steder det typisk smutter:
- Rå SQL og forespørgsler der går uden om modellerne.
- Baggrundsjob i køen, hvor der ikke er en logget ind bruger til at fortælle hvilken kunde det drejer sig om.
- Cache, filer og søgeindeks, som også skal være delt op pr. kunde.
- Unikke værdier. Et fakturanummer eller en e-mailadresse skal typisk være unik pr. kunde og ikke i hele databasen, så indekset skal indeholde
tenant_id.
Bruger du PostgreSQL, kan du lægge et ekstra sikkerhedsnet ned i selve databasen med row level security. Så afviser databasen rækker fra andre kunder, selv hvis koden har en fejl. Vær opmærksom på at ejeren af en tabel som standard går uden om reglerne, så applikationen skal forbinde med en databasebruger der ikke ejer tabellerne.
Model 2: ét schema pr. kunde
Et schema er en navngivet samling af tabeller inde i en database. I denne model får hver kunde sit eget sæt tabeller i sit eget schema, men alle schemas ligger i samme database på samme server. Koden behøver ikke filtrere på kunde-id, fordi den kun ser tabellerne i det schema den er sat til at bruge.
Det lyder som det bedste fra begge verdener, og for nogle projekter er det. Men der er to forbehold.
For det første er modellen i praksis mest relevant i PostgreSQL. I MySQL er CREATE SCHEMA et synonym for CREATE DATABASE, så her er schema pr. kunde i praksis det samme som database pr. kunde. Valget af database hænger altså tæt sammen med valget af tenancy-model, så tag dem samlet når du skal vælge tech stack til en SaaS.
For det andet skal alle migreringer køres én gang pr. schema. Med 500 kunder er det 500 migreringer ved hver udgivelse, og fejler nummer 312, har du kunder på to forskellige versioner af databasen. Mange tusinde schemas i samme database gør også backup og vedligehold tungere. Og bruger du connection pooling (en mellemstation der deler databaseforbindelser mellem forespørgsler), kræver det omtanke at skifte schema sikkert for hver forespørgsel.
Jeg vælger sjældent denne model. Den passer bedst når du har et moderat antal kunder, bruger PostgreSQL og har en reel grund til at holde tabellerne adskilt, fx at enkelte kunder skal have ekstra felter.
Model 3: én database pr. kunde
Her får hver kunde sin egen database. En central database holder styr på hvilke kunder der findes, hvad de betaler for, og hvilken database de hører til. Når en bruger logger ind, skifter applikationen forbindelse til kundens database.
Det er den model der giver den stærkeste adskillelse uden at give hver kunde sin egen installation. Den har nogle fordele som er svære at få på andre måder:
- Du kan gendanne én kunde fra backup uden at røre de andre. Har en kunde selv slettet noget vigtigt, er det en overskuelig opgave.
- En stor kunde kan flyttes til sin egen server, hvis den belaster systemet.
- Når en kunde stopper og skal have sine data slettet, sletter du databasen.
- Du kan placere en kundes database et bestemt sted, hvis kontrakten kræver det.
Prisen er drift. Hver migrering skal køres for hver kunde, hver database skal have backup og overvågning, og antallet af databaseforbindelser vokser. Statistik på tværs af kunder, fx et overblik over aktive kunder, kræver at du samler data i den centrale database eller i et separat analyseværktøj. Betaler du pr. database hos en cloududbyder, kan regningen også vokse hurtigt.
I Laravel ville jeg ikke bygge det fra bunden. Pakken Tenancy for Laravel understøtter en database pr. kunde, opretter automatisk databasen når en ny kunde bliver oprettet, og har en kommando der kører migreringer for alle kunder. Den kan også køre med schema pr. kunde i PostgreSQL eller med én delt database, så du ikke låser dig fast fra start.
Hvornår jeg vælger hvad
Jeg anbefaler en delt database i næsten alle nye SaaS-projekter. De fleste produkter skal hurtigt ud til de første betalende kunder, og her er den delte database den model der koster mindst at bygge, drive og ændre. Hvad der ellers skal med i første version, og hvad der kan vente, gennemgår jeg i indlægget om at afgrænse en SaaS-MVP.
Jeg anbefaler database pr. kunde fra start når mindst én af disse ting er sand:
- Du sælger til få, store kunder der skriver krav om adskilte data ind i kontrakten.
- Branchen har særlige krav, fx sundhed, finans eller det offentlige, og kunderne spørger til adskillelse i deres sikkerhedsspørgeskemaer.
- En enkelt kunde kan forventes at have mere data end resten tilsammen.
- Kunder skal kunne få deres data placeret i et bestemt land eller hos en bestemt udbyder.
Den model jeg oftest anbefaler på længere sigt, er en hybrid: en delt database til de fleste kunder og en dedikeret database til de få der betaler for det. Microsoft beskriver samme mønster, fx med prøvekunder i en delt database og premium-kunder i deres egen. Det kræver at koden bruger kunde-id overalt, også i de dedikerede databaser, så det er den samme kode der kører begge steder.
Og så er der tilfælde hvor du slet ikke skal bygge multi-tenant. Laver du et system til én virksomhed, er det et internt værktøj og ikke en SaaS. Har du tre store kunder der hver vil have deres egen version med egne tilpasninger, er separate installationer måske ærligere end at presse dem ind i ét produkt. Og skal du fra første dag håndtere tusindvis af kunder fordelt på flere regioner med krav om certificeringer, har du brug for et team med folk der kun laver drift, ikke kun en freelanceudvikler som mig.
Sådan bygger du det ind fra start: 7 trin
Uanset model er det de samme trin der afgør om multi-tenancy bliver en styrke eller en kilde til fejl.
- Definér hvad en tenant er. Er det en virksomhed, en afdeling eller et team? Kan en bruger høre til flere kunder, fx en revisor med mange klienter? Svaret former hele datamodellen, så skriv det ned før der bliver kodet.
- Afklar kravene til adskillelse. Spørg dine første kunder, eller kig i de kontrakter og udbud du vil vinde. Databehandleraftale, placering af data og sletning hører med her, og min GDPR-tjekliste til SaaS er et godt sted at starte.
- Vælg model og skriv begrundelsen ned. Én side er nok. Så kan den næste udvikler se hvorfor valget blev truffet, og hvornår det bør tages op igen.
- Sæt kunde-id på alle tabeller fra første dag. Også hvis du vælger en database pr. kunde. Det koster næsten intet nu og gør det muligt at flytte kunder mellem modeller senere.
- Gør kunden til en del af alt, ikke kun databasen. Baggrundsjob, cache, filer, søgning, e-mails, logs og fejlrapporter skal alle kende den aktuelle kunde.
- Test adskillelsen automatisk. Min anbefaling er at oprette to kunder i testene og tjekke at kunde B aldrig kan se, ændre eller eksportere kunde A's data. Testen skal køre ved hver ændring, ikke kun før lancering.
- Planlæg backup og gendannelse pr. kunde. Øv dig i at gendanne én kundes data, før en kunde beder om det. I indlægget om backup af webapps gennemgår jeg hvordan du sætter det op, så gendannelsen faktisk virker.
Før du åbner for den første betalende kunde
- Alle tabeller med kundedata har et kunde-id, og forespørgsler bliver filtreret automatisk.
- Unikke indeks og nummerserier, fx fakturanumre, er unikke pr. kunde.
- Baggrundsjob, cache, filer og søgeindeks er delt op pr. kunde.
- Administratorværktøjer der kan se på tværs af kunder, er begrænset til få personer, og brugen bliver logget.
- En automatisk test beviser at én kunde ikke kan se en andens data.
- Du har prøvet at gendanne og slette én kundes data.
- Valget af model og begrundelsen står skrevet ned.
Næste skridt
Er du ved at planlægge en SaaS, så træf beslutningen om tenancy-model sammen med valget af database og hosting, og før den første kunde bliver oprettet. Har du allerede et produkt hvor kunderne deler tabeller uden kunde-id, kan det stadig rettes. Det kræver bare en plan for at flytte data uden nedetid.
Vil du se hvordan jeg griber sådan et projekt an, med et forprojekt til fast pris som første skridt, så læs om min udvikling af SaaS-produkter.
Ofte stillede spørgsmål
Kan jeg skifte model senere?
Ja, men det bliver dyrere jo længere du venter. Har alle tabeller et kunde-id fra start, er det overskueligt at flytte én kunde fra den delte database til sin egen: du kopierer kundens rækker over og skifter forbindelsen. Mangler kunde-id'et, skal du først finde ud af hvilke data der hører til hvem, og det er ofte det største arbejde.
Kræver GDPR at hver kunde har sin egen database?
Nej. GDPR artikel 32 kræver passende tekniske og organisatoriske foranstaltninger i forhold til risikoen, men foreskriver ikke en bestemt databasemodel. En delt database med god adgangskontrol og tests kan sagtens leve op til kravene. Dine kunder kan dog stille strengere krav i deres kontrakter. Det her er ikke juridisk rådgivning, så tal med en jurist hvis du er i tvivl.
Hvordan ved systemet hvilken kunde en bruger hører til?
Typisk ud fra login eller adressen. Den enkleste løsning er at brugeren er knyttet til en kunde, og at systemet slår den op ved login. Mange SaaS-produkter bruger også et subdomæne pr. kunde, fx kunde.ditprodukt.dk, eller lader store kunder bruge deres eget domæne. Kan en bruger høre til flere kunder, skal der være en tydelig måde at skifte mellem dem.
Hvor mange kunder kan en delt database klare?
Langt flere end de fleste SaaS-produkter når op på, hvis databasen er bygget ordentligt. Det afgørende er sjældent antallet af kunder, men om indeksene starter med kunde-id, og om enkelte kunder kører meget tunge forespørgsler. Bliver én kunde en belastning for de andre, kan du flytte netop den kunde til sin egen database, hvis koden er forberedt til det.