İçeriğe geç

URL Kısaltıcı Tasarımı — Parçaları Tek Sistemde Birleştirmek

İleri 10 dk Çok sık karşılaşılır

Önce şunu oku: SQL İndeksleme ve EXPLAIN , Cache Stratejileri — Defter Ne Zaman Yalan Söyler? , Rate Limiting — Kapıdan Saniyede Kaç Kişi Geçer?

30 saniyede özet

Bir sistemi tasarlamak kutu çizmekle değil, birkaç çarpmayla başlar. URL kısaltıcıda sayılar okuma ağırlıklı bir sistem gösterir; asıl sorular kısa kodun nasıl üretileceği ve yönlendirmenin nasıl hızlı tutulacağıdır.

Tiyatroda paltonu vestiyere verdiğinde görevli sana küçük bir fiş uzatır. Uzun paltoyu taşımazsın, cebinde yalnızca bir numara vardır.

Küçük fiş, doğru palto, hızlı teslim.
Adım adım oku
  1. Uzun paltoyu verirsin, karşılığında küçük bir fiş alırsın.
  2. Fiş numarası, paltonun asıldığı yerle birlikte deftere yazılır.
  3. Fişi gösteren paltosunu geri alır.
  4. En çok istenen paltolar tezgâhın hemen dibinde asılı durur.
  1. Bayt: Pazarlama, SMS'lere sığacak kısa kampanya linkleri istiyor. Günde milyonlarca!

  2. Sen: Kafka, Kubernetes, beş mikroservis... Nereden başlasam?

  3. Bayt: Kutulardan değil. Önce birkaç soru, sonra birkaç çarpma.

  4. Bayt: Bu derste yeni bir şey öğrenmeyeceksin; bildiklerini birleştireceksin.

Önce soru, sonra kutu

Önce ne yapılacağı netleşir: uzun adresi kısaltmak, kısa kodla yönlendirmek, tıklamaları saymak. Sonra kaba hesap gelir; kesin olması gerekmez, büyüklüğü doğru olsun yeter.

yeni link günde 1 milyon → saniyede ≈ 12 yazma (1.000.000 / 86.400)
tıklama link başına 100 → saniyede ≈ 1.200 okuma
beş yıl 1 milyon × 365 × 5 → ≈ 1,8 milyar kayıt

Sayılar iki şey söyler: sistem okuma ağırlıklı ve veri tek bir veritabanına sığar. Kafka ya da shard’lı veritabanı bu ölçekte bir sorunu çözmez.

Kafam karıştı, daha basit anlat

Önce ne istendiğini yaz, sonra üç çarpma yap. Sayılar sana hangi parçaya ihtiyacın olduğunu söyler.

Hızlı kontrolBaşlangıç

Bir sistemi tasarlamaya başlarken ilk adım hangisidir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Günde 1 milyon yeni kısa link oluşuyor ve her link ortalama 100 kez açılıyor. Saniyedeki ortalama yönlendirme sayısı yaklaşık kaçtır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kısa kodu kim üretir?

Kodlar base62Sayıları 0-9, a-z ve A-Z olmak üzere 62 karakterle yazmak. Kısa ve URL'de sorunsuz kodlar üretir; her ek karakter kod sayısını 62 katına çıkarır.Sözlükte gör → ile yazılır: rakamlar, küçük ve büyük harfler. 1,8 milyar kayıt için 6 karakter yeter, çünkü 62⁶ yaklaşık 56,8 milyar eder.

Kısa kodu şöyle üretiyorsun: uzun adresin SHA-256 hash'ini base62'ye çevir, ilk 7 karakteri al. Hash'ler benzersiz olduğuna göre bu güvenli mi? Cevabı göster

Değil. Hash’in tamamı neredeyse benzersizdir ama ilk 7 karakteri değildir. Milyarlarca linkte farklı adresler aynı kodu alır; kontrol yoksa yeni link eskisinin üstüne yazılır.

Çakışma sanılandan erken gelir; buna doğum günü paradoksuRastgele seçilen değerlerin, sanılandan çok daha erken çakışması. 23 kişilik bir grupta iki kişinin aynı günde doğmuş olma olasılığı yarıyı geçer.Sözlükte gör → denir. Güvenli iki yol var.

Sayaç her sayıyı bir kez verir; instance’lar sayaçtan bin sayılık bloklar alıp kendi içinde harcar. Rastgele kod ise çakışabilir, ama veritabanındaki benzersizlik kuralı çakışmayı yakalar ve yeni bir kod denenir.

Kafam karıştı, daha basit anlat

Hash’in bir parçasını kesmek kod çakıştırır. Ya her numarayı bir kez veren bir sayaç kullan ya da rastgele seç ve çakışırsa yeniden dene.

Hızlı kontrolOrta

Beş yılda yaklaşık 1,8 milyar kısa link birikecek. Kodlar base62 (a-z, A-Z, 0-9) ile yazılırsa en az kaç karakter gerekir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kısa kodu şöyle üretiyorsun: uzun adresin SHA-256 hash'ini base62'ye çevir, ilk 7 karakteri al. Milyarlarca link olunca ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Satır satır: bir tıklamanın yolu

Okuma yazmanın yüz katı, üstelik tıklamaların çoğu birkaç popüler linke gidiyor. Kod → adres eşlemesi de neredeyse hiç değişmiyor. Bu, cache stratejileri dersindeki cache-asideUygulamanın önce cache'e baktığı, bulamazsa veritabanından okuyup cache'e koyduğu düzen. Cache veritabanının yanında durur; düşerse uygulama veritabanından devam eder.Sözlükte gör → için biçilmiş kaftan.

Kısa koddan uzun adrese

RedirectController.java
1@GetMapping("/{code}")
2ResponseEntity<Void> redirect(@PathVariable String code) {
şu an çalışan satır String target = cache.get(code);
4 if (target == null) {
5 target = links.findTarget(code).orElseThrow(LinkNotFound::new);
6 cache.set(code, target, Duration.ofHours(24));
7 }
8 clicks.publish(new Clicked(code, Instant.now()));
9 return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(target)).build();
10}

Debug

Adım 1/5

tıklama Önce cache. Popüler kodlar burada; çoğu tıklama veritabanına hiç uğramaz.

code
= aZ3k9Q
Java 21UTF-8LF3:1

Sol/sağ ok tuşlarıyla da gezebilirsin.

301 kalıcı yönlendirmedir ve tarayıcılar onu saklayabilir. Servisin yükü azalır, ama sonraki tıklamalar sayılmaz ve yanlış bir hedef tarayıcılarda takılı kalabilir.

Hızlı kontrolOrta

Her kısa linkin kaç kez tıklandığını saymak istiyorsun. Yönlendirme için hangi HTTP durum kodu daha uygun?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Sayıları sen seç: ölçek, okuma oranı, kısa kodun üretimi ve cache. Her adım bir satır hesap.

URL kısaltıcı: zarfın arkasına hesap

Tohum 1

Oynat ya da adımla: her adım bir satır hesap.

Hız
Adım 0

Şu an ne oldu?

Kutuları çizmeden önce birkaç çarpma

Kaç yazma, kaç okuma, kaç kayıt? Cevaplar hangi parçaya ihtiyaç olduğunu söyler.

Görevler0/3

  • Günde 100 milyon linki beş yıl boyunca kaç karakterle karşıladığını göraçık

    İpucu

    Ölçeği en büyüğe çek.

  • Kısa kodların sessizce çakıştığı bir tasarım kuraçık

    İpucu

    Kodu adresin hash'inden kes, kontrol ekleme.

  • En büyük ölçekte çakışmasız, tahmin edilemez ve cache'li bir tasarım kuraçık

    İpucu

    Rastgele kod, benzersizlik kontrolü ve okuma yolunda cache.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanı oynat. Günde 100 milyon link, kod hash’ten kesiliyor, cache yok. Çakışma kesin ve veritabanı her tıklamayı taşıyor.
  2. Kısa kodu “Sayaç + base62” yap. Çakışma bitti, ama kodlar sıralı.
  3. “Rastgele kod + benzersizlik kontrolü” seç ve cache’i aç. Veritabanına giden okuma onda bire iner.
  4. Ölçeği küçült. Kod uzunluğunun ve depolamanın nasıl değiştiğine bak.
Hızlı kontrolOrta

Okumalar yazmaların 100 katı ve linklerin küçük bir kısmı tıklamaların büyük kısmını alıyor. Veritabanını rahatlatmak için ilk kaldıraç hangisi?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tuzaklar

Sıralı kodlar taranır. Sayaçtan gelen kod aZ3k9Q ise sonraki aZ3k9R’dir; biri kodları sırayla deneyip başkalarının linklerini bulabilir. Özel içerik için kod rastgele olur ve kısa link hiçbir zaman yetkilendirmenin yerini tutmaz.

Oluşturma ucu kötüye kullanılır. Açık bir kısaltıcı, zararlı siteleri gizlemek için birebirdir. Oluşturma rate limit ile sınırlanır ve hedef adresler kontrol edilir.

Hash’e güvenip üzerine yazmak. Kimliği elle verilen bir entity’de save, satır varsa onu günceller. Çakışan bir kod, eski linkin hedefini sessizce değiştirir; ekleme “yoksa ekle” diye yapılır.

Kafam karıştı, daha basit anlat

Kodu rastgele seç, kimin ne kadar link açtığını sınırla, var olan bir kodun üstüne asla yazma.

Hızlı kontrolİleri

Bazı kullanıcılar kısa linklerinin başka bir siteye gittiğini söylüyor. Kodu incele.

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Hatalı satıra dokun, sonra kontrol et.

ShortLinkService.java
Java 21UTF-8LF
Derinleş · Kampanya linkleri: şema, kod üretimi ve yönlendirme 4 dosya · ~83 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/main/resources/db/migration/ V1__short_link.sql Şema: kod birincil anahtar, yönlendirme tek indeks aramasıdır. PostgreSQL'in varsayılan karşılaştırması büyük ve küçük harfi ayırır. MySQL'in varsayılan collation'ı ayırmaz; orada aZ3 ile Az3 aynı kod sayılır.

src/main/resources/db/migration/V1__short_link.sql
CREATE TABLE short_link (
code VARCHAR(10) PRIMARY KEY,
target TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

src/main/java/com/bank/links/ Base62.java Kod: SecureRandom ile 7 karakter. Tahmin edilemez; çakışma olasılığı hesaplanabilir ve küçüktür.

src/main/java/com/bank/links/Base62.java
final class Base62 {
private static final String ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final SecureRandom RANDOM = new SecureRandom();
private Base62() {
}
static String random(int length) {
StringBuilder code = new StringBuilder(length);
for (int i = 0; i < length; i++) {
code.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
}
return code.toString();
}
}

src/main/java/com/bank/links/ ShortLinkService.java Servis: 'yoksa ekle' ile yazılır. Çakışan kod hiçbir zaman eski linkin üstüne yazılmaz; yeni kod denenir.

src/main/java/com/bank/links/ShortLinkService.java
@Service
class ShortLinkService {
private static final int CODE_LENGTH = 7;
private static final int MAX_ATTEMPTS = 3;
private final JdbcTemplate jdbc;
ShortLinkService(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
String shorten(String target) {
for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
String code = Base62.random(CODE_LENGTH);
// An existing code is never overwritten: on conflict nothing is inserted and we try again.
int inserted = jdbc.update("""
INSERT INTO short_link (code, target) VALUES (?, ?)
ON CONFLICT (code) DO NOTHING
""", code, target);
if (inserted == 1) {
return code;
}
}
throw new IllegalStateException("No free short code after " + MAX_ATTEMPTS + " attempts");
}
}

src/main/java/com/bank/links/ RedirectController.java Yönlendirme: cache-aside ve 302. Tıklama sayımı kuyruğa bırakılır, yönlendirme onu beklemez.

src/main/java/com/bank/links/RedirectController.java
@RestController
class RedirectController {
private static final Duration CACHE_TTL = Duration.ofHours(24);
private final StringRedisTemplate redis;
private final JdbcTemplate jdbc;
private final ApplicationEventPublisher clicks;
RedirectController(StringRedisTemplate redis, JdbcTemplate jdbc, ApplicationEventPublisher clicks) {
this.redis = redis;
this.jdbc = jdbc;
this.clicks = clicks;
}
@GetMapping("/{code}")
ResponseEntity<Void> redirect(@PathVariable String code) {
String key = "link:" + code;
String target = redis.opsForValue().get(key);
if (target == null) {
target = jdbc.query("SELECT target FROM short_link WHERE code = ?",
rs -> rs.next() ? rs.getString(1) : null, code);
if (target == null) {
return ResponseEntity.notFound().build();
}
redis.opsForValue().set(key, target, CACHE_TTL);
}
// Counted later in bulk; the redirect never waits for it.
clicks.publishEvent(new LinkClicked(code, Instant.now()));
return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(target)).build();
}
}
record LinkClicked(String code, Instant at) {
}

Kendini sına

Önce hızlı bir ısınma: puan yok, kayıt yok. Sonra asıl sorular.

Şimşek turu1/5

Bir tasarıma başlamanın en iyi yolu, önce bütün kutuları çizmektir.

Soru 1/3İleri

Kodları bir sayaçtan üretiyorsun ve on instance var. Her yeni link için veritabanından sıradaki sayıyı istemek bir darboğaz oldu. Yaygın çözüm hangisi?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aklında kalacak üç şey

  1. 1 Tasarım gereksinim ve kaba hesapla başlar. Günlük sayıyı 86.400'e bölmek saniyelik yükü, beş yıllık toplam da kod uzunluğunu ve depolamayı verir.
  2. 2 Hash'in ilk birkaç karakteri benzersiz değildir. Benzersizlik ya sayaçtan gelir ya da veritabanı kuralıyla yakalanan çakışmadan ve yeni denemeden.
  3. 3 Okuma ağırlıklı sistemde ilk kaldıraç, yönlendirme yolundaki cache'tir. Tıklama sayılacaksa 302 döner, çünkü 301 tarayıcıda saklanır.
Sonraki kapı Ödeme servisin yüzde 99.9 ayakta olmalı. Bu, ayda kaç dakika kesintiye izin var demek? SLO ve Hata Bütçesi — Ne Kadar Bozuk Olmaya İzin Var? · 9 dk

4 kart sonraki derste seni bekliyor

0/4 kart bu dersten toplandı