Systemdesign af URL-forkortningstjeneste
Når vi har brug for en ny nøgle, kan vi tage et af de allerede genererede ID’er. Denne fremgangsmåde kan gøre tingene hurtigere, da vi, mens der kommer en ny anmodning, ikke behøver at oprette et ID, sikre dets entydighed osv. UGS vil sikre, at alle ID’er er unikke, og de kan gemmes i en database, så ID’erne ikke behøver at blive genereret hver gang.
Som vi har brug for en byte til at gemme et tegn, kan vi gemme alle disse nøgler i:
6 (tegn) * 68.7B (unikke nøgler) ~= 412 GB.
★ Tilgængelighed & Pålidelighed:
Hvis vi beholder én kopi af UGS, er der tale om et enkelt fejlpunkt. Så vi er nødt til at lave en kopi af UGS. Hvis den primære server dør, kan den sekundære server håndtere brugernes anmodninger.
Hver UGS-server kan cache nogle nøgler fra key-DB. Det kan fremskynde tingene. Men vi skal være forsigtige; hvis en server dør, før alle nøglerne er opbrugt, mister vi disse nøgler. Men vi kan antage, at dette er acceptabelt, da vi har næsten 68B unikke nøgler på seks bogstaver.
For at sikre tilgængelighed skal vi sikre, at vi fjerner et enkelt fejlpunkt i systemet. Replication for Data vil fjerne et enkelt fejlpunkt og give backup. Vi kan holde flere replikationer for at sikre databaseserverens pålidelighed. Og for at sikre uafbrudt service har andre servere også brug for kopier.
★DataStorage:
I dette system skal vi gemme milliarder af poster. Hvert objekt, vi opbevarer, er muligvis mindre end 1 KB. En URL-data er ikke relateret til en anden. Så vi kan bruge en NoSQL-database som Cassandra, DynamoDB osv. Et NoSQL-valg ville være lettere at skalere, hvilket er et af vores krav.
★ Skalerbarhed:
For at understøtte milliarder af URL’er skal vi partitionere vores database for at opdele og gemme vores data i forskellige DB-servere.
i) Vi kan partitionere databasen baseret på det første bogstav i hashnøglen. Vi kan lægge nøgler, der begynder med “A”, på en server og “B” på en anden server. Dette kaldes Range Based Partitioning.
Problemet med denne fremgangsmåde er, at den kan føre til en ubalanceret partitionering. Der er f.eks. meget få ord, der begynder med “Z.” På den anden side kan vi have for mange URL’er, der begynder med bogstavet “E.”
Vi kan kombinere mindre hyppigt forekommende bogstaver i én databasepartition.
ii) Vi kan også partitionere baseret på hash’en af de objekter, vi lagrer. Vi kan tage hash’en af nøglen til at bestemme den partition, som vi kan gemme dataobjektet i. Hashfunktionen vil generere et servernummer, og vi gemmer nøglen på denne server. Denne proces kan gøre fordelingen mere tilfældig. Dette er Hash-baseret partitionering.
Hvis denne fremgangsmåde stadig fører til overbelastede partitioner, skal vi bruge Consistent Hashing.
★ Cache:
Vi kan cache URL’er, der ofte tilgås af brugerne. UGS-serverne kan, inden de foretager en forespørgsel til databasen, kontrollere, om cachen indeholder den ønskede URL-adresse. Så behøver den ikke at foretage forespørgslen igen.
Hvad sker der, når cachen er fuld? Vi kan erstatte et ældre, ubrugt link med en nyere eller populær URL. Vi kan vælge LRU-politikken (Least Recently Used) til at fjerne cachen fra vores system. I denne politik fjerner vi først den URL, der er brugt mindst for nylig.
★ Load balancer:
Vi kan tilføje et load balancing-lag forskellige steder i vores system foran URL-forkortningsserveren, databasen og cacheserverne.
Vi kan bruge en simpel Round Robin-tilgang til fordeling af anmodninger. I denne fremgangsmåde fordeler LB indgående anmodninger ligeligt mellem backend-servere. Denne tilgang til LB er enkel at gennemføre. Hvis en server er død, vil LB holde op med at sende trafik til den.
Problem: Hvis en server er overbelastet, vil LB ikke holde op med at sende en ny anmodning til den pågældende server i denne tilgang. Vi får måske brug for en intelligent LB senere.
★ Linkudløb efter en varighed:
Hvis udløbstiden er nået for en URL-adresse, hvad sker der så med linket?
Vi kan søge i vores datastores og fjerne dem. Problemet her er, at hvis vi vælger at søge efter udløbne links for at fjerne dem fra vores datalager, vil det lægge et stort pres på vores database.
Vi kan gøre det på en anden måde. Vi kan langsomt fjerne udløbne links med jævne mellemrum. Selv om nogle døde links lever længere, bør det aldrig returneres til brugerne.
Hvis en bruger forsøger at få adgang til et udløbet link, kan vi fjerne linket og returnere en fejl til brugeren. Der kan køres en periodisk oprydningsproces for at fjerne udløbne links fra vores database. Da lagerplads bliver billigere, kan nogle links forblive der, selv om vi savner, mens vi rydder op.
Når vi har fjernet linket, kan vi lægge det tilbage i vores database til genbrug.
★ Sikkerhed:
Vi kan gemme adgangstypen (offentlig/privat) med hver URL i databasen. Hvis en bruger forsøger at få adgang til en URL, som han ikke har tilladelse til, kan systemet sende en fejl (HTTP 401) tilbage.