Problem
Marketinq komandası https://sho.rt/aZ3kP9x kimi linklər istəyir ki, uzun kampaniya URL-lərinə yönləndirsin, üstəlik neçə nəfərin klik etdiyini görmək istəyir. Servis sadə görünür və məhz buna görə populyar dizayn məsələsidir: nüvə çox kiçikdir, ona görə müzakirə onun ətrafındakı seçimlər üzərində gedir. Oxuma yazmadan qat-qat çoxdur, redirect gecikməsini hər istifadəçi hiss edir, sui-istifadə (spam, phishing) isə real təhlükədir.
Tələblər
Əvvəlcə nəyin daxil, nəyin kənar olduğunu yazın.
Funksional
- Uzun URL üçün qısa link yaratmaq, istəyə görə fərdi alias və bitmə tarixi ilə.
- Qısa linki hədəfinə yönləndirmək.
- Əsas klik analitikası: link üzrə say, gün üzrə təxmini bölgü.
Qeyri-funksional
- Redirect-lər sürətli və yüksək əlçatan olmalıdır; link yaratmaq bir qədər çox gecikməyə dözə bilər.
- Qısa kodlar ardıcıllıqla təxmin edilməməlidir (gizli linklərin siyahılanmasının qarşısını almaq üçün).
- Link səssizcə hədəfini dəyişməməlidir.
Bu versiyada kənarda qalanlar: istifadəçi hesabları, billing, klik üzrə fərdiləşdirmə.
Təxmini hesablamalar
Bu rəqəmlər metodu göstərmək üçün fərziyyələrdir, hansısa real servisin faktları deyil. Ayda 10 milyon yeni link və oxuma/yazma nisbəti 100:1 götürək.
- Yazma: 10 000 000 / (30 x 86 400 san) təxminən saniyədə 4.
- Oxuma: bunun 100 mislidir, orta hesabla saniyədə təxminən 400. Pik üçün 10 qat fərz etsək, saniyədə təxminən 4 000.
- Yaddaş: ayda 10 milyon link, indekslər daxil sətir başına təxminən 500 bayt, ayda təxminən 5 GB, ildə 60 GB edir. Bu, ilk illərdə tək PostgreSQL primary-yə rahat sığır.
Nəticə faydalıdır: bu, data həcmi mülayim olan, oxuma ağırlıqlı sistemdir. Yükü cache və read replica-lar həll edir; sharding birinci gündən lazım deyil.
API
POST /api/v1/links
{ "targetUrl": "https://shop.test/very/long?utm=spring",
"alias": "spring-sale", (optional)
"expiresAt": "2027-01-01T00:00:00Z" } (optional)
-> 201 { "code": "aZ3kP9x", "shortUrl": "https://sho.rt/aZ3kP9x" }
GET /{code}
-> 302 Location: https://shop.test/very/long?utm=spring
-> 404 if unknown, 410 if expired or disabled
GET /api/v1/links/{code}/stats?from=2026-09-01&to=2026-09-30
-> 200 { "total": 1234,
"byDay": [ { "date": "2026-09-01", "clicks": 40 } ] }Hər klikin servisinizə gəlməsini (analitika üçün) və hədəfi sonradan dəyişmək imkanını saxlamaq istəyirsinizsə, 302 (müvəqqəti) işlədin. 301 brauzerlər tərəfindən agressiv cache olunur; bu yükü azaldır, amma klikləri gizlədir və sonrakı dəyişiklikləri çətinləşdirir.
Data modeli
CREATE TABLE short_link ( code varchar(12) PRIMARY KEY, target_url text NOT NULL, owner_id bigint, created_at timestamptz NOT NULL DEFAULT now(), expires_at timestamptz, disabled boolean NOT NULL DEFAULT false);code üzərindəki primary key əsas yolu, yəni dəqiq açarla axtarışı təmin edir. Klik datası başqa formadadır (yalnız əlavə olunan, yüksək həcmli), ona görə onu ayrı saxlayın, məsələn asinxron doldurulan click_daily(code, day, clicks) cədvəlində.
Kodların generasiyası
62 simvoldan (a-z, A-Z, 0-9) ibarət 7 simvollu kod 62^7, yəni təxminən 3,5 trilyon kombinasiya verir; bu, fərz etdiyimiz həcmdən çox böyükdür. Üç geniş yayılmış yanaşma:
| Yanaşma | Güclü tərəfi | Zəif tərəfi |
|---|---|---|
| Təsadüfi kod, insert, toqquşmada təkrar | Sadə, təxmin edilməz | Cədvəl dolduqca təkrarlar artır; unique constraint lazımdır |
| Base62-yə çevrilmiş sayğac | Toqquşma yoxdur, qısadır | Ardıcıl və təxmin edilə bilər, əgər rəqəm qarışdırılmasa |
| Bloklarla paylanan əvvəlcədən yaradılmış açar hovuzu | Sürətli, qaynar nöqtə yoxdur | İdarə edilməli əlavə komponent |
Bu həcm üçün unique constraint ilə təsadüfi kodlar kifayətdir: yarat, INSERT et, unique pozulanda yeni kodla təkrarla. Kodlar təxmin edilməməlidirsə, sadə sayğacdan çəkinin; işlədirsinizsə, onu geri qaytarıla bilən qarışdırma addımından keçirin.
Oxuma yolu və cache
Redirect axını açarla axtarışdır, ona görə cache-ə uyğundur. Caching strategiyaları məqaləsindəki kimi cache-aside işlədin:
Browser -> CDN/edge -> Redirect service -> Redis -> PostgreSQL
| miss: read DB, populate Redis (TTL)
| hit: respond 302 immediately
v
Click event -> Kafka -> aggregator
-> click_dailySkanerlər bazanı sıxmasın deyə mövcud olmayan kodların nəticəsini də (negative cache) qısa müddətə saxlayın. TTL-i söndürülmüş linkin əlçatan qala biləcəyi müddətdən qısa seçin və ya link söndürüləndə açarı açıq şəkildə silin.
Redirect-i yavaşlatmadan analitika
Hər klikdə verilənlər bazasına sinxron yazmayın. Kiçik klik hadisəsini (kod, vaxt, qaba metadata) Kafka kimi broker-ə göndərin, aggregator isə gündəlik sayğacları yeniləsin. Redirect sürətli qalır; analitika eventual consistent olur, bu da dashboard-lar üçün məqbuldur. Şəxsi datayla bağlı ehtiyatlı olun: yalnız məhsulun ehtiyac duyduğunu saxlayın, IP və user agent barədə qərarı toplamazdan əvvəl verin.
Oxumanın miqyaslanması və əlçatanlıq
- Load balancer arxasında bir neçə stateless redirect instance işlədin.
- Verilənlər bazasının qarşısına Redis qoyun; cache miss-lər üçün read replica-ları düşünün.
- CDN və ya edge cache-i yalnız söndürülmüş linklərin daha gec yayılmasına razısınızsa qoyun.
- Yük profilləri fərqlənirsə, yaratma və redirect-i ayrı deploy vahidləri saxlayın.
code hash-ı üzrə sharding yalnız hesablamalar ciddi səhv çıxsa aktuallaşır; hash əsaslı açar bu sonrakı keçidi asanlaşdırır.
Sui-istifadənin qarşısını almaq
Qısa linklər təyinatı gizlədir, ona görə phishing üçün cəlbedicidir. Ağlabatan nəzarətlər:
- yaratmanı hesab və IP üzrə rate-limit etmək
- URL sintaksisini yoxlamaq və yalnız
httpvəhttpssxemlərinə icazə vermək - komandanın saxladığı və ya abunə olduğu reputasiya siyahısı ilə məlum pis domenləri bloklamaq
- linki tez bildirmək və söndürmək yolu, yuxarıda qeyd olunan cache silinməsi ilə
- servisin özünə işarə edən hədəfləri rədd edərək redirect dövrlərinin qarşısını almaq
Ucadan deyilməli güzəştlər
- 301 və 302: performans, nəzarət və analitikaya qarşı.
- Təsadüfi və sayğac kodları: sadəlik və gizlilik, toqquşmaların idarəsinə qarşı.
- Sinxron və asinxron analitika: anlıq dəqiqlik, redirect gecikməsinə qarşı.
- Fərdi alias-lar: daha dostcasına linklər, amma ad sahəsi tutula bilər və yaradılmış kodlarla toqquşur; fərqli uzunluq və ya prefiks ayırın.
- Bitmə tarixi: təmizləmə job-u və ya oxuma zamanı lazy yoxlama tələb edir; ikisi də burada ucuzdur.
Uğursuzluq ssenariləri
Nə pozula biləcəyini də yazın. Redis əlçatmazdırsa, redirect servisi birbaşa PostgreSQL-ə düşür; bazanın bu yükü daşıya bilməsi üçün qısa timeout və rate limit lazımdır. Verilənlər bazasının primary-si dəyişirsə, yaratma bir müddət uğursuz ola bilər, amma oxuma replica və cache-dən davam edir. Kafka əlçatmaz olarsa, klik hadisələri yerli bufer və ya itkiyə razılıqla atılmalıdır; redirect bunu gözləməməlidir. Hər halda əsas qayda budur: yalnız analitika xarab olmalı, redirect yox.
Tez-tez edilən səhvlər
- Yükü hesablamadan sharding və multi-region replikasiya dizayn etmək.
- Verilənlər bazasını klik sayğacı kimi işlətmək və populyar linklərdə yazma qaynar nöqtəsi yaratmaq.
- Yaratmanın idempotent olmadığını unutmaq; client təkrarı iki kod yarada bilər. Idempotency pattern-ləri kimi idempotency key qəbul edin.
- Sui-istifadəni buraxılışdan sonraya saxlamaq.
Yoxlama siyahısı
- Tələblər və kənarda qalanlar yazılıb
- Hesablamalar fərziyyə kimi işarələnib və nəticə çıxarılıb
- Naməlum və vaxtı bitmiş linklər üçün status kodlu API
- Kod generasiyası strategiyası və toqquşma idarəsi seçilib
- Söndürülmüş linklər üçün invalidation ilə cache strategiyası
- Asinxron analitika yolu
- Sui-istifadə nəzarətləri və linki götürmə yolu
Növbəti addım
Bu nümunənin arxasındakı təkrarlanan metodu system design müsahibə metodu məqaləsində öyrənin, caching strategiyalarına baxın və ya Senior Backend Developer roadmap-ını izləyin.