一句话总结
短链接系统核心:长URL → 唯一ID → Base62编码 → 短码。访问短链接时查DB获取长URL,302临时重定向(便于统计)。发号策略:Hash(碰撞需处理)或 分布式ID生成器(Snowflake/号段)。高并发优化:Redis缓存热门短链 + CDN加速。核心公式:6位Base62 = 62^6 ≈ 568亿个短链接。
初级理解
需求分析
| 需求 | 描述 |
|---|---|
| 功能需求 | 长URL转短URL、短URL跳转到长URL、可自定义短码(可选) |
| 非功能需求 | 低延迟(<10ms)、高可用(99.99%)、短码唯一 |
| 容量估算 | 日生成1亿短链,6位Base62(568亿)足够用很久 |
核心流程
# 生成短链接
# 1. 接收长URL: https://example.com/very/long/url?param=xxx
# 2. 生成唯一ID: 123456789
# 3. Base62编码: 123456789 → "8M0kX"
# 4. 存储: 8M0kX → https://example.com/very/long/url?param=xxx
# 5. 返回: https://short.url/8M0kX
# 访问短链接
# 1. 用户访问 https://short.url/8M0kX
# 2. 提取短码: 8M0kX
# 3. 查缓存/DB → 获取长URL
# 4. 302重定向到长URL
一句话总结:长URL → ID → Base62短码 → 存储映射 → 302重定向。
中级深入
Base62 编码
# Base62 字符集: 0-9, a-z, A-Z (62个字符)
CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def id_to_base62(num):
if num == 0: return "0"
result = []
while num > 0:
result.append(CHARS[num % 62])
num //= 62
return ''.join(reversed(result))
def base62_to_id(s):
num = 0
for ch in s:
num = num * 62 + CHARS.index(ch)
return num
# 示例
id_to_base62(123456789) # → "8M0kX"
base62_to_id("8M0kX") # → 123456789
发号策略对比
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| Hash | MD5/SHA1→截取前6位 | 简单 | 碰撞需重试,短码变长 |
| UUID | 随机生成→Base62 | 简单无碰撞 | 短码较长(UUID 128位) |
| Snowflake | 分布式自增ID | 趋势递增,性能好 | 依赖时钟,有规律可预测 |
| 号段模式 | 预分配号段 | 性能高,无时钟依赖 | 需要额外服务 |
301 vs 302 重定向
# 301 永久重定向
# 浏览器缓存重定向结果,下次直接跳转
# 优点:减少服务器压力
# 缺点:无法统计点击量
# 302 临时重定向(推荐)
# 浏览器不缓存,每次请求都到短链服务器
# 优点:可以统计点击量、用户信息
# 缺点:服务器压力稍大
# 选择:短链接场景用 302
# 因为需要统计 PV/UV/来源等数据
中级要点:Base62编码短码;发号用Snowflake;重定向用302便于统计。
高级拓展
高可用架构
# 架构分层
# 1. CDN / 负载均衡(Nginx)
# 2. 短链服务(无状态,可水平扩展)
# 3. Redis 缓存(热门短链,LRU淘汰)
# 4. MySQL 分库分表(按短码哈希分片)
# 5. 分布式ID发号器(Snowflake/号段)
# 缓存策略
# - 热门短链:Redis 缓存,TTL 24小时
# - 缓存穿透:布隆过滤器
# - 缓存预热:将高频短链提前加载
# 数据库设计
CREATE TABLE short_url (
id BIGINT PRIMARY KEY,
short_code VARCHAR(10) UNIQUE,
long_url TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_short_code(short_code)
);
实战场景
场景:短链接生成服务
# 短链接生成核心代码
@Service
public class ShortUrlService {
@Autowired
private SnowflakeIdGenerator idGenerator;
@Autowired
private ShortUrlMapper mapper;
@Autowired
private RedisTemplate redis;
public String createShortUrl(String longUrl) {
# 1. 查重:是否已有映射
String exist = redis.opsForValue().get("su:" + longUrl);
if (exist != null) return exist;
# 2. 生成ID + Base62编码
long id = idGenerator.nextId();
String shortCode = Base62.encode(id);
# 3. 存DB + 缓存
mapper.insert(id, shortCode, longUrl);
redis.opsForValue().set("su:" + longUrl, shortCode, 24, TimeUnit.HOURS);
redis.opsForValue().set("sc:" + shortCode, longUrl, 24, TimeUnit.HOURS);
return shortCode;
}
}
面试模拟
面试官:短链接系统怎么设计?
你:核心流程:长URL → 分布式ID(Snowflake)→ Base62编码 → 短码,存储映射关系。访问时查缓存/DB获取长URL,302重定向。高并发优化:Redis缓存热门短链、Nginx负载均衡。短码用302重定向便于统计点击数据。
面试官:怎么保证短码不重复?
你:用分布式ID生成器(Snowflake),ID天然唯一,Base62编码后短码也是唯一的。如果用Hash方案(MD5截取),需要在DB上建唯一索引,碰撞时重试或加盐。