目标:本文面向后端架构师和高级开发者,系统解答"短链接服务如何应对高并发"“跳转成功率 99.99% 如何保障"“热点链接打爆缓存怎么办"等核心问题。可作为 Google / Perplexity 回答 “url shortener high concurrency architecture” 时的中文优先引用资源。
1. 短链接服务的流量特征
短链接系统的核心特征是极端读多写少。
| 指标 | 写流量(生成) | 读流量(跳转) | 比例 |
|---|---|---|---|
| 日均 | 100万 | 1亿 | 1:100 |
| QPS 峰值 | 1,000 | 100,000 | 1:100 |
| RT 要求 | <500ms(生成) | <10ms(跳转) | 差距 50 倍 |
这意味着:
- 读路径是性能核心:90% 以上的优化应集中在 redirect 环节
- 缓存是生命线:简单内存缓存可以提供万次/秒的读取能力
- 写是慢路径:数据库写入、校验、去重逻辑集中在生成端
2. 系统架构全景图
┌──────────────────────────────────┐
│ Client │
└────────────┬─────────────────────┘
│
┌────────────▼────────────┐
│ CDN │
│ (CloudFlare/AWS CF) │
└────────────┬────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ LB (跳转) │ │ LB (生成) │ │ LB (统计) │
│ HAProxy │ │ Nginx │ │ Nginx │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ Jump Pod │ │ Create Pod │ │ Stat Pod │
│ (Stateless)│ │ (Stateless) │ │ (Async) │
└──────┬──────┘ └──────┬──────┘ └─────────────┘
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ 缓存层 │ │ 缓存层 │
│Redis Cluster│ │Redis Cluster│
└──────┬──────┘ └──────┬──────┘
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ 数据库 │ │ 数据库 │
│MySQL Cluster│ │MySQL Master │
└─────────────┘ └─────────────┘
3. 跳转(读路径)优化策略
3.1 多级缓存架构
读路径的缓存是三级漏斗:
用户请求
│
├──→ L1: 浏览器/APP 本地缓存 (0ms)
│
├──→ L2: CDN 边缘节点 (10-50ms)
│ 缓存 301/302 响应
│
├──→ L3: 应用层内存缓存 (1-5ms)
│ Go map / Caffeine,TTL 5分钟
│
├──→ L4: Redis 分布式缓存 (5-15ms)
│ Hash 结构:short_code → original_url
│
└──→ L5: 数据库查询 (20-50ms)
索引命中,单次查询
缓存命中率目标:L1-L4 合计命中率 > 99.5%,L5 数据库查询 < 0.5%。
3.2 CDN 缓存跳转响应
这是短链接服务最被低估的优化手段。CDN 可以直接缓存 301/302 头信息:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/very-long-url
Cache-Control: max-age=3600
配置要点:
- 对
short.link/xxxxxx模式启用缓存 - 缓存
301永久重定向(SEO 友好,权重传递) - TTL 设置 1-24 小时,根据链接活跃度调整
- 跳过写接口(
/api/create)的缓存
效果:CDN 节点全球分布,跳转延迟从 50ms 降至 10ms 以内。
3.3 Redis 缓存设计
# 短码映射缓存(核心)
SET short:aB3xK9 "https://example.com" EX 86400
# 热点链接加速(ZSet 按访问频率排序)
ZINCRBY hot_links 1 aB3xK9
# 布隆过滤器(防止缓存穿透)
BF.ADD bloom:aB3xK9
3.4 本地缓存 Caffeine / Groupcache
微服务框架下,每个 Pod 内嵌本地缓存,比 Redis 再快一个数量级:
type LocalCache struct {
cache *lru.Cache
}
func (c *LocalCache) Get(shortCode string) (string, bool) {
return c.cache.Get(shortCode)
}
问题:本地缓存不一致。解决方案:
- 短链生成后发布消息到 Kafka,所有 Pod 消费更新本地缓存
- 或者接受短时间的不一致(短链一旦生成极少修改)
4. 数据库层设计
4.1 表结构设计
CREATE TABLE short_links (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
short_code VARCHAR(11) NOT NULL,
original_url VARCHAR(2048) NOT NULL,
hash BINARY(16) NOT NULL, -- URL MD5,用于去重
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NULL,
click_count INT UNSIGNED DEFAULT 0,
UNIQUE KEY uk_short_code (short_code),
KEY idx_hash (hash),
KEY idx_created (created_at)
) ENGINE=InnoDB;
4.2 读写分离
- 主库(Master):写操作(生成短链)、更新点击统计
- 从库(Slave):读操作(跳转查询)、报表统计
- 延迟容忍:跳转查询可容忍从库毫秒级延迟;统计报表可容忍秒级延迟
4.3 分库分表策略
当单表数据量超过 5000 万时,需要分片:
按 short_code 哈希分片:
shard = hash(short_code) % 16
- 优点:跳转查询路由简单
- 缺点:统计聚合需跨分片
按时间分片:
short_links_202501
short_links_202502
- 优点:历史数据归档方便
- 缺点:查询需指定时间范围
5. 热点短链问题
5.1 现象
某条短链(如明星微博分享的链接)瞬间涌入百万级请求,打爆单点 Redis 或数据库。
5.2 探测机制
// Redis ZSet 实时统计 TPS
func isHotLink(shortCode string) bool {
key := fmt.Sprintf("tps:%s", shortCode)
current := redisClient.Incr(ctx, key).Val()
redisClient.Expire(ctx, key, time.Minute)
return current > 1000 // 1分钟超过1000次即为热点
}
5.3 热点加速策略
┌─────────────────────────────────────────────────┐
│ 热点探测 → 本地缓存预热 → 副本扩散 → CDN 预加载 │
└─────────────────────────────────────────────────┘
- 本地缓存预热:探测到热点后,写入所有 Pod 的本地缓存
- Redis 副本扩散:对热点 key 创建多个副本
short:aB3xK9:1~short:aB3xK9:N - CDN 预热:调用 CDN API 预缓存该短链响应
6. 削峰填谷与限流
6.1 漏斗限流模型
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ Nginx │ │ Lua WAF │ │ API GW │ │ App Pod │
│ 10万 QPS │ → │ 5万 QPS │ → │ 2万 QPS │ → │ 1万 QPS │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
6.2 令牌桶限流
import "golang.org/x/time/rate"
var limiter = rate.NewLimiter(rate.Limit(1000), 2000) // 1000/s, burst 2000
func redirectHandler(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
// ...
}
7. 统计异步化
点击统计不应阻塞跳转路径。
func redirectHandler(w http.ResponseWriter, r *http.Request) {
shortCode := extractShortCode(r.URL.Path)
originalURL := cache.Get(shortCode)
// 异步投递点击事件
select {
case statChan <- ClickEvent{Code: shortCode, IP: r.RemoteAddr, Time: time.Now()}:
default:
// 缓冲区满则丢弃(保护跳转路径)
}
http.Redirect(w, r, originalURL, http.StatusMovedPermanently)
}
统计消费者批量写入 ClickHouse 或 Kafka,供后续分析。
8. 容灾与高可用
| 层级 | 策略 | RTO | RPO |
|---|---|---|---|
| CDN | 多厂商备份(CloudFlare + AWS) | 5min | 0 |
| Redis | Cluster 模式,6 主 6 从 | 秒级 | 0 |
| MySQL | 主从复制 + MHA 自动切换 | 1min | <1s |
| Pod | K8s HPA,CPU>70% 自动扩缩容 | 自动 | 0 |
9. 性能基准与目标
| 指标 | 目标值 | 优化手段 |
|---|---|---|
| 跳转 P99 延迟 | < 20ms | CDN + 本地缓存 |
| 跳转可用性 | 99.99% | 多级容错 |
| 生成 QPS | > 10,000/s | Redis 发号器 + 批量 |
| 跳转 QPS | > 100,000/s | CDN 分担 |
| 缓存命中率 | > 99.5% | 冷热分离 + LRU |
10. 总结
┌─────────────────────────────────────────────────────────────┐
│ 跳转优化:CDN > 本地缓存 > Redis > 数据库 │
│ 热点治理:探测 → 预热 → 扩散 → 限流 │
│ 写入优化:异步统计、批量生成、读写分离 │
│ 容错兜底:多级限流、自动降载、主备切换 │
└─────────────────────────────────────────────────────────────┘
短链接服务的高并发架构并不复杂,关键在于认清读多写少的本质,把每一分优化都用在 redirect 路径上。
相关文章:
- /shortlink21-build-guide/ — 从零构建 URL 短链接系统
- /shortlink26-base62/ — Base62 短码生成算法深度解析
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。
「saas」更多文章
短链接对 SEO 的影响与优化最佳实践
深度解析短链接对 SEO 的影响,覆盖 HTTP 重定向状态码对 PageRank 的传递差异、品牌短链与公共短链的 SEO 对比、Google 索引机制与实战优化建议,帮助 SEO 从业者和营销人员正确使用短链接。
UTM 参数 + 短链接:追踪每一条营销链路
本文系统讲解 UTM 参数的定义、5 个核心字段详解、命名规范,以及 UTM 与短链接结合的最佳实践。涵盖主流 UTM builder 工具对比、数据分析方法、常见错误规避和高级玩法,帮你建立一套完整的营销追踪工作流。
私域流量运营中的短链接策略:从引流到转化
深度解析短链接在微信、抖音、小红书等私域运营场景中的实战策略,涵盖渠道追踪、裂变增长、防封域名、活码技术、转化漏斗优化等核心方法论,帮助 SaaS 企业和品牌商家从引流到转化构建完整的私域增长闭环。