URLショートナーシステム設計:リンク短縮サービスの内部動作
「URLショートナーを設計してください」はシステム設計インタビューの最も人気のある質問の一つです。その理由は正当で、ハッシング、データベース、キャッシング、ロードバランシング、分散システムなど、一見単純な製品に多くの技術が関わっているためです。
このガイドでは、URLショートナーが実際にどのように機能するか、主な設計上の決定事項、そしてスケール時のトレードオフについて説明します。
基本的なフロー
URLショートナーは2つのことを行います:
- 1短縮: 長いURLを取得して短いコードを生成する
- 2リダイレクト: ユーザーが短いURLにアクセスすると、元のURLにリダイレクトする
高レベルのフローは以下の通りです:
ユーザーが短いリンクを作成:
長いURL → 短いコードを生成 → マッピングを保存 → 短いURLを返す
ユーザーが短いリンクをクリック:
短いURL → コードを検索 → 長いURLを取得 → 301リダイレクト
短いコードの生成
中心的な課題は、ユニークで短いコードを生成することです。いくつかのアプローチがあります:
アプローチ1:Base62エンコーディング
自動増分IDをBase62文字列に変換します。使用する文字は[a-zA-Z0-9]です:
- ID
1→1 - ID
62→10 - ID
238,328→ZZZ
7文字のBase62コードは62^7 = 3.5兆のユニークなURLをサポートします。
長所: シンプル、予測可能な長さ、衝突がない 短所: シーケンシャルIDは予測可能です(ユーザーは他の短いURLを推測できます)
アプローチ2:ハッシング
ハッシュ関数(MD5、SHA-256)を長いURLに適用し、最初のN文字を取得します:
SHA256("https://example.com/very/long/url") → "a3f2b8c1..." 短いコード:"a3f2b8c"
長所: 同じ入力は常に同じ出力を生成します(重複排除) 短所: ハッシュ衝突の処理が必要です。固定ハッシュ長は領域を無駄にする可能性があります
アプローチ3:ランダム生成
ランダムな英数字文字列を生成し、ユニーク性をチェックします:
長所: シンプル、予測不可能 短所: 作成のたびに衝突チェックが必要です。データベースが満杯になると遅くなります
どのアプローチを使用するか?
ほとんどの本番システムは分散IDジェネレータを使用したBase62エンコーディングを使用しています。これはシンプル、衝突がなく、パフォーマンスが良好です。Linklyでも同様のアプローチを使用しています。より技術的でない概要については、URLショートナーがどのように機能するかをご覧ください。
データベース設計
中心的なテーブルはシンプルです:
urls ├── id(主キー、自動増分) ├── short_code(ユニークインデックス) ├── long_url(宛先) ├── created_at(タイムスタンプ) ├── user_id(作成者) └── click_count(非正規化カウンター)
SQLとNoSQL
SQL(PostgreSQL、MySQL): ACID準拠、強い一貫性、中規模スケールに最適。ほとんどのURLショートナーはここから始まります。
NoSQL(DynamoDB、Cassandra): 数十億のURLに対する水平スケーリングに優れています。このユースケースではイベンチュアル一貫性で問題ありません。
ハイブリッド: URLマッピング用のSQL(リダイレクト用に強い一貫性が必要)、クリック分析用のNoSQLまたはタイムシリーズデータベース(書き込み量が多く、イベンチュアル一貫性で問題ありません)。
リダイレクトの処理
ユーザーが短いリンクをクリックすると、システムは次を実行する必要があります:
- 1URLから短いコードを解析する
- 2対応する長いURLを検索する
- 3HTTPリダイレクトレスポンスを返す
301対302リダイレクト
- 301(永続的): ブラウザがリダイレクトをキャッシュします。サーバーリクエストが減りますが、リピートクリックの可視性が失われます。
- 302(一時的): ブラウザは毎回サーバーに確認します。リクエストが増えますが、クリック追跡の精度が向上します。
ほとんどのURLショートナーはクリック追跡の精度のために302リダイレクトを使用し、SEOユースケースのオプションとして301を提供します。この違いの詳細については、301リダイレクトに関するガイドを参照してください。
キャッシング
リダイレクトは高速である必要があります。レイテンシーの1ミリ秒がユーザーエクスペリエンスに影響します。キャッシングは重要です:
メモリ内キャッシュ(Redis/Memcached)
short_code → long_urlのマッピングをメモリにキャッシュします:
GET /abc123 → Redisで"abc123"を確認 → キャッシュヒット?リダイレクトを即座に返す → キャッシュミス?データベースを問い合わせ、結果をキャッシュ、リダイレクトを返す
小さなRedisインスタンスは何百万ものURLマッピングをキャッシュできます。ほとんどのトラフィックが比較的少数の人気リンクに向かっているため、キャッシュヒット率が90%を超えることは一般的です。
CDNキャッシング
301リダイレクトの場合、CDNエッジノードはリダイレクトレスポンスをキャッシュでき、元のサーバーに全くヒットすることなく、ユーザーに最も近い場所からこれを提供できます。
分析とクリック追跡
クリックデータを記録することは、リダイレクトを遅くしてはいけない書き込み集約的な操作です:
非同期処理
- 1ユーザーが短いリンクをクリック
- 2システムはリダイレクトを即座に返す
- 3クリックイベントはメッセージキューにプッシュされます(Kafka、RabbitMQ、SQS)
- 4バックグラウンドワーカーがイベントを処理します:ユーザーエージェントを解析、IPをジオロケーション、分析を保存
これにより、高速なリダイレクトパスが遅い分析パイプラインから分離されます。
キャプチャするデータ
- タイムスタンプ
- IPアドレス(ジオロケーション用)
- ユーザーエージェント(デバイス/ブラウザ検出用)
- リファラーヘッダー
- 国、都市(IPジオロケーションから)
スケーリングの考慮事項
読み取り集約的なワークロード
URLショートナーは極度に読み取り集約的です。典型的な比率は読み取り100:書き込み1です。これは以下を意味します:
- リダイレクトパスを最優先に最適化する
- キャッシングを積極的に使用する
- データベースの読み取りレプリカ
分散ID生成
複数のサーバーにわたって自動増分IDを使用する場合、衝突を避ける必要があります。オプション:
- Snowflake ID: Twitterのアプローチ — タイムスタンプ、マシンID、シーケンス番号を埋め込む
- UUID: 普遍的にユニークですが、より長い
- IDの範囲: 各サーバーに割り当てるIDの範囲を割り当てる
地理的分散
複数のリージョンにリダイレクトサーバーをデプロイします。東京のユーザーがバージニアのサーバーへのラウンドトリップを必要としてはいけません。
セキュリティの考慮事項
URLショートナーは悪用されてフィッシングやマルウェア配布に使用される可能性があります。本番システムには以下が必要です:
- URLスキャン — 宛先をマルウェアとフィッシングデータベースに照らしてチェック
- レート制限 — 悪意のある短いリンクの大量作成を防止
- 虐待報告 — ユーザーが疑わしいリンクを報告できるようにする
- プレビューページ — オプションでリダイレクト前にユーザーにリンクがどこに向かうかを表示
追加機能
基本的な短縮とリダイレクト以上に、本番URLショートナーは以下を追加します:
- カスタムドメイン — 自分のドメインを使用したブランド化された短いリンク
- カスタムスラッグ — ランダムな文字の代わりに独自の短いコードを選択する
- 有効期限 — 時間制限されたリンクは特定の日付の後に機能を停止
- パスワード保護 — 宛先にアクセスするためにパスワードを要求する
- A/Bテスト — 複数の宛先間をローテーション
- ジオターゲティング — 国別にリダイレクト
- デバイスターゲティング — モバイルとデスクトップ用の異なる宛先
- QRコード — 任意の短いリンク用にスキャン可能なコードを生成
結論
URLショートナーシステム設計は優れた演習です。シンプルに見えますが、複雑さの層が明らかになります:コード生成、データベース設計、キャッシング、分析パイプライン、虐待防止です。これらの基礎知識を理解することは、インタビューの準備をしているか、独自のツールを構築しているかに関わらず役に立ちます。
構築せずに本番URLショートナーを使用したい場合? Linklyで始める — 上記で説明されたすべてのアーキテクチャが、カスタムドメイン、分析、高度な機能と共に使用可能です。
