亿级流量短链接平台架构设计笔记。
核心业务流
短链接平台的核心业务分为两个阶段:生成 和 跳转。
生成链路
用户提交原始长 URL,系统返回一个唯一的短码(如 https://s.wx.qq.com/aBcDeF)。
- 用户请求生成短链接(含原始 URL 与自定义参数)。
- 系统校验合法性(URL 格式、域名白名单、安全扫描)。
- 调用分布式 ID 生成器获取唯一 ID。
- 将 ID 通过 Base62 编码转为短码。
- 存储映射关系(短码 → 长 URL + 元数据)。
- 写入缓存。
- 返回短链接。
跳转链路
用户访问短链接时,系统完成从短码到原始 URL 的查找和 301/302 跳转。
- DNS 解析到接入层。
- 负载均衡分发到网关。
- 网关提取短码,转发至 Redirect Service。
- 查询缓存(多级缓存架构)。
- 缓存命中 → 返回目标 URL → 浏览器 301/302 跳转。
- 缓存未命中 → 回源数据库 → 回填缓存 → 跳转。
- 异步发送点击事件到消息队列,用于后续分析。
关键技术架构
分布式 ID 生成
短码是系统的核心标识,需要满足全局唯一、生成高效、趋势递增(便于分库分表)的要求。
Snowflake 方案
经典的雪花算法生成 64-bit 整数:
| 位段 | 长度 | 说明 |
|---|---|---|
| 符号位 | 1 bit | 固定为 0 |
| 时间戳 | 41 bit | 毫秒级时间戳,可用 69 年 |
| 工作机器 ID | 10 bit | 支持 1024 个节点 |
| 序列号 | 12 bit | 毫秒内自增,单节点每毫秒支持 4096 个 ID |
改进点:
- 时钟回拨问题:记录上次生成时间,回拨时阻塞等待或借用预留序列号。
- 机器 ID 动态分配:通过注册中心(ZooKeeper/etcd)自动分配和过期回收。
- 趋势递增:时间戳在高位,天然适合 MySQL/B+Tree 索引顺序写入。
Base62 编码
将 Snowflake ID(或其他唯一 ID)转为短码:
- 62 个字符集:
[a-z, A-Z, 0-9](排除混淆字符如0/O/l/I)。 - 6-7 位短码即可覆盖数百亿 ID($62^6 \approx 568,亿$)。
- 对应关系:
0 → a, 1 → b, ..., 61 → 9(自定义映射表,可增加反爬混淆)。
存储架构
读流量(短码查询)—— 缓存优先
| 层级 | 技术 | 用途 | 容量 | 延迟 |
|---|---|---|---|---|
| L1 | 本地内存(Caffeine/Guava) | 热链接缓存 | MB 级 | < 1ms |
| L2 | Redis Cluster | 全量缓存 | GB ~ TB | 1~5ms |
| L3 | MySQL/NoSQL | 持久化存储 | TB 级 | 5~20ms |
Key 设计:
短码 → { longUrl, createdAt, expiresAt, ownerId }
Example: aBcDeF → { "longUrl": "https://...", "createdAt": 1721600000, ... }
写流量 —— 异步批处理
- 短链接写入采用 先写 DB,再异步删缓存 的 Cache-Aside 模式。
- 使用版本号或 CAS 机制保证缓存更新的最终一致。
- 写请求先经消息队列削峰填谷,再逐批写库(避免瞬间写爆 DB 连接池)。
分库分表策略
- 水平分表:以短码哈希或 Snowflake ID 区间分片。
- 分库数量:按预估总链接数 × 增长速率规划(如 32 库 × 128 表,约 4096 个分片)。
- 路由策略:
hash(shortCode) % shardCount,或基于一致性哈希。 - 归档策略:过期链接定期迁入冷存储(HBase/OSS + Hive),减少热库数据量。
高并发处理
负载均衡
Client → DNS (GeoDNS) → LVS (四层) → Nginx (七层) → Gateway → Service
- DNS 层:GeoDNS 将用户调度到最近的区域入口,初步分流。
- LVS(四层):基于 IP + 端口分发,毫秒级转发,支持 DDoS 防护。
- Nginx(七层):按短码哈希或 URL 路径分流,支持限流、黑白名单。
- 网关层:统一鉴权、限流(令牌桶/漏桶)、熔断、降级。
微服务拆分
| 服务 | 职责 | 部署策略 |
|---|---|---|
| Shorten Service | 生成短链接、校验 | 弹性伸缩(根据写入 QPS) |
| Redirect Service | 短码跳转查询 | 多副本部署,CPU 密集 |
| Admin Service | 链接管理、数据统计 | 低频率,可独立扩缩 |
| Analytics Service | 点击流分析、报表 | 异步消费消息队列 |
服务间通过 RPC(gRPC/Thrift)通信,服务发现依赖 Consul/K8s DNS。
消息队列削峰
跳转链路的点击事件和写请求通过消息队列进行异步处理:
- Kafka/RocketMQ:高吞吐消息队列,处理点击事件流。
- RabbitMQ:延迟消息 + 死信队列,处理链接过期、定时删除等任务。
- Pulsar(可选):支持分层存储和多租户,适用于超大规模场景。
优化策略
热链接处理
热点链接(如大促活动链接)在海量用户同时点击时需要特殊处理。
多级缓存
热链接识别:
- 访问计数器(Redis ZSet + 滑动窗口)统计每个短码的 QPS。
- QPS 超过阈值(如 1000/s)时标记为热链接,主动推送到各节点本地缓存。
- 本地缓存使用 Caffeine(Guava Cache 升级版),支持基于访问频率的淘汰(W-TinyLFU),抗突发流量。
本地缓存一致性:
- 设置较短 TTL(如 30~60s),允许短暂不一致。
- 通过 Redis Pub/Sub 或配置中心(Nacos/Apollo)广播失效通知。
- 写请求涉及特定短码时,广播删除该节点本地缓存。
301 vs 302 跳转
| 状态码 | 浏览器缓存 | 适用场景 | 说明 |
|---|---|---|---|
| 301 | 永久缓存 | 永久链接、匿名短链 | 浏览器缓存跳转结果,后续请求直接跳转,服务端压力小 |
| 302 | 不缓存 | 推广链接、A/B 测试 | 每次请求都经过服务端,可统计点击量、校验状态、动态调整 |
生产建议:默认使用 302 跳转,配合异步记录点击日志。对于需要极致性能的场景(如短信/推送中的固定链接),可改用 301 + CDN 加速。
容错设计
多活部署
- 异地多活(Active-Active):核心区域(华北、华东、华南)各部署一套完整服务。
- 流量跨区域调度:DNS 流量调度 + 全局负载均衡(GSLB),区域故障时自动切换。
- 数据同步:MySQL 主主同步 / TiDB 跨区域部署,Redis 主动复制 + 消息队列补齐。
缓存容错
| 故障场景 | 策略 | 说明 |
|---|---|---|
| Redis 宕机 | 本地缓存 → 直连 DB | 本地缓存持续提供服务,DB 限流保护 |
| Redis 分片故障 | 降级到同分片副本 / 一致性哈希漂移 | 少量短码不可用,大部分正常 |
| 缓存雪崩 | 过期时间加随机偏移 + 本地缓存兜底 | 防止批量过期导致 DB 被打满 |
| 缓存击穿 | 互斥锁(分布式锁)控制回源 | 单短码同时只有一个线程回源 DB |
| 缓存穿透 | 布隆过滤器 + 空值缓存 | 拦截不存在短码的查询 |
服务熔断与降级
- 熔断:Redirect Service 调用 DB 失败率超过阈值 → 熔断器打开 → 直接返回失败/降级页 → 定时探活恢复。
- 限流:基于令牌桶或滑动窗口,按用户/IP/短码维度限流。
- 降级:
- 三级降级:正常(Redis + 本地缓存) → 仅本地缓存 → 仅静态 CDN(预置热门链接)。
- 写降级:生成请求直接返回 503,保护跳转链路的可用性。
依赖治理
- 每个依赖调用都设置超时(连接超时 200ms、读超时 500ms)。
- 采用指数退避重试,最多重试 2 次。
- 设置线程池隔离(Hystrix/Sentinel 信号量),防止单个依赖拖垮整个服务。
- 链路追踪(Jaeger/Zipkin)记录每次跳转的完整调用链,快速定位故障点。
总结
短链接系统的核心挑战在于 极高读 QPS + 写一致性 + 高可用:
- 读:通过多级缓存(本地 → Redis → DB)和 CDN 预热,将 P99 延迟控制在 10ms 以内。
- 写:通过消息队列削峰、异步批处理、分库分表,支撑单日千万级短链接创建。
- 高可用:通过异地多活、熔断降级、依赖超时治理,保证 99.99% 以上的可用性。
- 扩展性:基础架构可在中型项目(日 PV 千万级)与大型项目(日 PV 百亿级)之间平滑扩展,差异主要体现在资源容量和组件选型上。
参考:
- Twitter Snowflake ID 设计
- Redis Cluster 分片原理
- Caffeine Cache W-TinyLFU 淘汰策略
- 《数据密集型应用系统设计》