如何设计一个短链接系统?

2025年 阅读约 15 分钟 面试指南 · 系统设计

深入解析短链接系统设计:需求分析与容量估算、Hash+Base62编码方案、分布式ID发号器(Snowflake)、301 vs 302重定向选择、高可用与缓存方案,附面试模拟问答。

一句话总结

短链接系统核心:长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

发号策略对比

方案原理优点缺点
HashMD5/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上建唯一索引,碰撞时重试或加盐。