题目
设计一个高并发的短网址生成服务
信息
- 类型:问答
- 难度:⭐⭐
考点
系统架构设计, 高并发处理, 数据存储选型, 分布式ID生成, 缓存策略
快速回答
设计要点:
- 核心流程:长URL通过分布式ID生成器转换为短码(如Base62编码)
- 存储方案:Redis缓存热点数据 + MySQL持久化存储
- 高并发处理:使用负载均衡 + 无状态服务层
- 关键优化:Bloom过滤器防止恶意长URL重复生成
- 扩展性:通过分片键(如短码首字母)水平拆分数据库
1. 系统架构设计
核心组件:
- API网关:处理路由、限流(如每秒10万请求)
- 无状态服务层:处理URL转换逻辑,可横向扩展
- 存储层:Redis(缓存热点URL映射)+ MySQL(持久化存储)
- ID生成服务:Snowflake算法或Redis INCR生成全局唯一ID
2. 核心流程
# 短码生成伪代码示例(Base62编码)
def generate_short_url(long_url):
# 1. 检查Bloom过滤器(防止重复长URL)
if bloom_filter.might_contain(long_url):
# 查询DB是否已存在
existing = db.query_by_long_url(long_url)
if existing: return existing.short_code
# 2. 生成全局唯一ID(示例:Snowflake ID)
unique_id = id_generator.next_id() # e.g. 9876543210
# 3. 转换为Base62短码
base62_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
short_code = ""
while unique_id > 0:
unique_id, rem = divmod(unique_id, 62)
short_code = base62_chars[rem] + short_code
# 4. 存储映射关系(Redis + MySQL)
redis.setex(f"url:{short_code}", long_url, 3600) # 缓存1小时
db.insert_mapping(short_code, long_url)
return short_code # e.g. "3ke7W"3. 关键技术点
- 分布式ID生成:
- Snowflake方案:时间戳(41bit) + 机器ID(10bit) + 序列号(12bit)
- Redis方案:
INCR global:url_id配合Lua脚本保证原子性
- 缓存策略:
- 读写比例约9:1(读远高于写),采用Cache-Aside模式
- Redis缓存最近访问的URL,设置TTL自动过期
- 存储优化:
- MySQL分片:按短码首字母分库(如0-9,a-z,A-Z分26个库)
- 表结构:
(short_code CHAR(6) PK, long_url TEXT, created_at TIMESTAMP)
4. 高并发处理
- 读优化:
- 301重定向代替302(减轻服务端压力)
- CDN缓存高频短链接
- 写优化:
- 批量ID预生成(缓解实时生成压力)
- 消息队列削峰(如Kafka缓冲突发写入)
5. 常见错误与解决方案
- 短码冲突:确保ID生成器全局唯一(ZooKeeper协调机器ID)
- 缓存穿透:Bloom过滤器拦截无效长URL + 缓存空值
- 热点Key:短码分片存储 + 本地缓存(如Caffeine)
- 安全风险:
- 限制同一IP生成频率
- 敏感词过滤长URL
6. 扩展知识
- 容量估算:
- 10亿条记录 ≈ MySQL 500GB(考虑索引)
- Redis集群:100万QPS需16节点(每个节点6万QPS)
- 监控指标:重定向延迟、缓存命中率、ID生成速率
- 进阶优化:
- 自定义短码(额外校验逻辑)
- 数据冷热分离(HBase存历史数据)