
系统设计:可扩展的短链接服务
需求
功能性:
- 缩短 URL(自定义或生成别名)
- 将短 URL 重定向到原始 URL
- 分析(点击次数、来源、地理位置)
- 链接过期
非功能性:
- 每天创建 1 亿个 URL(写入:约 1200/s)
- 每天 100 亿次重定向(读取:约 115,000/s)—— 100:1 读写比
- 重定向可用性 99.99%
- P99 重定向延迟 < 50ms

数据估算
存储:
- 每天 1 亿个新 URL x 365 天 x 5 年 = 1825 亿个 URL
- 每个 URL:约 500 字节(ID + 原始 URL + 元数据)
- 总计:约 90TB
流量:
- 峰值 115,000 次重定向/秒
- 80/20 规则:20% 的 URL 承载 80% 的流量
URL 缩短算法
import string, hashlib
ALPHABET = string.ascii_letters + string.digits # 62 个字符
BASE = 62
def encode(num: int) -> str:
result = []
while num > 0:
result.append(ALPHABET[num % BASE])
num //= BASE
return ''.join(reversed(result)) or ALPHABET[0]
def generate_short_code(url: str, user_id: str) -> str:
input_str = f"{url}{user_id}"
hash_value = hashlib.md5(input_str.encode()).hexdigest()
num = int(hash_value[:8], 16)
return encode(num).ljust(7, ALPHABET[0])[:7]

数据库设计
CREATE TABLE urls (
id BIGSERIAL PRIMARY KEY,
short_code VARCHAR(10) UNIQUE NOT NULL,
long_url TEXT NOT NULL,
user_id BIGINT REFERENCES users(id),
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP
);
CREATE INDEX idx_short_code ON urls(short_code);
-- 分析(单独的表/服务)
CREATE TABLE url_clicks (
id BIGSERIAL PRIMARY KEY,
short_code VARCHAR(10) NOT NULL,
clicked_at TIMESTAMP DEFAULT NOW(),
ip_address INET,
country CHAR(2)
);
架构
客户端 -> CDN/负载均衡器 -> API 服务器(无状态)
|
写入路径 | 读取路径
| | |
验证服务 | Redis 缓存(TTL=24h)
| | 命中 -> 返回 URL
PostgreSQL(主) | 未命中 -> 读取数据库 -> 缓存
|
分析:API -> Kafka -> ClickHouse(批量聚合)

缓存策略
async def get_long_url(short_code: str) -> str:
cache_key = f"url:{short_code}"
cached = await redis.get(cache_key)
if cached:
return cached.decode()
url = await db.fetchone("SELECT long_url FROM urls WHERE short_code = $1", short_code)
if not url:
raise NotFoundException(short_code)
# 抖动 TTL 以防止惊群效应
import random
ttl = 86400 + random.randint(0, 3600)
await redis.setex(cache_key, ttl, url['long_url'])
return url['long_url']
扩展考虑
- 读取扩展:热门链接使用 CDN,Redis 集群作为缓存
- 写入扩展:按 short_code 范围分片数据库,预生成代码池
- 分析:使用 Kafka + ClickHouse,不拖慢重定向
- 地理分布:多区域部署,本地缓存
短链接服务是一个典型的读密集型系统,受益于积极缓存。