百万级QPS短链服务架构设计(高可用+低延迟)
一、核心指标要求
指标 | 目标值 | 实现手段 |
---|---|---|
QPS | ≥1,000,000 | 分布式架构+多级缓存 |
延迟 | P99 <10ms | 内存计算+本地缓存 |
可用性 | 99.99% (年故障<52分钟) | 多活部署+自动故障转移 |
存储容量 | 百亿级短链 | 分片存储+冷热分离 |
抗峰值 | 3倍日常流量波动 | 弹性扩缩容+队列削峰 |
二、高并发架构设计
graph TD
A[客户端] --> B[SLB]
B --> C[API集群]
C --> D[本地缓存]
C --> E[分布式缓存]
C --> F[ID生成器]
E --> G[分库分表存储]
H[监控告警] --> C
H --> E
H --> G
1. 关键组件说明
-
全局负载均衡:DNS轮询+智能调度(如AWS Route53)
-
API服务器:Go服务实例(500节点,每节点2k QPS)
-
ID生成器 :雪花算法(Snowflake)优化版
go// 改进的Snowflake ID结构(每秒可生成400万ID) // [1bit保留][41bit毫秒时间][10bit机器ID][12bit序列号] func GenerateID() uint64 { return (time.Now().UnixNano() << 22) | (machineID << 12) | atomic.AddUint32(&seq, 1) % 4096 }
-
缓存层级 :
- L1: 本地缓存(LRU,50ms TTL)
- L2: Redis集群(Proxy模式,30min TTL)
- L3: 存储层缓存(SSD Cache)
2. 数据流设计
-
生成短链:
bashPOST /create?url=https://long-url.com → 生成62进制Hash(原始URL+盐值) → 写Redis SETNX "short:abc123" "原URL" EX 86400 → 异步写入DB
-
访问短链:
bashGET /s/abc123 → 检查本地缓存 → L1命中则返回 → 查询Redis Cluster → L2命中则回源更新L1 → 查DB并重建缓存(布隆过滤器预处理无效请求) → 302重定向到原URL
三、关键优化手段
1. 低延迟保障
优化点 | 技术实现 | 效果提升 |
---|---|---|
内存化 | 热点数据常驻内存(如Go全局map+locksharding) | 减少90% Redis调用 |
零拷贝 | 直接返回HTTP 302不解析Body | 降低CPU消耗30% |
协议优化 | 采用HTTP/2+QUIC减少握手开销 | 延迟降低15%~20% |
智能DNS | 基于用户地理位置返回最近接入点 | 网络延迟减少40% |
2. 高可用设计
-
多活部署 :
bash# 单元化部署结构(同城双活+异地灾备) Region A (上海) : AZ1 - API(100) + Redis(主) + MySQL(主) AZ2 - API(100) + Redis(备) + MySQL(备) Region B (北京) : AZ3 - API(50) + Redis(灾备) + MySQL(只读副本)
-
熔断降级 :
go// Go代码示例:基于Hystrix的熔断 hystrix.ConfigureCommand("redis_get", hystrix.CommandConfig{ Timeout: 500, // 毫秒 MaxConcurrentRequests: 1000, ErrorPercentThreshold: 50, }) err := hystrix.Do("redis_get", func() error { return redis.Get(key) }, nil)
3. 存储优化
-
分库分表 :
sql-- 按短链Hash分64库,每个库分16表 CREATE TABLE `short_url_${db_idx}_${tbl_idx}` ( `id` BIGINT UNSIGNED PRIMARY KEY, `short_key` CHAR(8) COLLATE utf8_bin UNIQUE, `original_url` VARCHAR(2048), `expire_time` TIMESTAMP, KEY `idx_short_key` (`short_key`) ) ENGINE=InnoDB;
-
冷热分离 :
- 热数据:TiKV(3副本)
- 冷数据:对象存储(S3)+ 压缩存储
四、抗峰值方案
1. 流量缓冲
go
// Kafka削峰示例
func CreateShortLink(url string) {
select {
case msgChan <- url: // 正常处理
metrics.Inc("queue_len")
default: // 队列满时降级
go func() {
redis.RPush("backup_queue", url)
}()
}
}
// 后台Worker消费
for url := range msgChan {
processURL(url)
metrics.Dec("queue_len")
}
2. 弹性扩缩容
bash
# K8s HPA规则(基于QPS自动扩容)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: shortlink-api
minReplicas: 100
maxReplicas: 500
metrics:
- type: External
external:
metric:
name: requests_per_second
selector: {matchLabels: {app: shortlink}}
target:
type: AverageValue
averageValue: 2000
五、生产级监控
graph LR
A[Prometheus] --> B[QPS]
A --> C[P99延迟]
A --> D[缓存命中率]
B --> E[Grafana大盘]
C --> E
E --> F[AlertManager]
F -->|短信/邮件| G[运维人员]
关键报警项:
- Redis集群命中率 <80%持续5分钟
- MySQL主从延迟 >500ms
- 单节点QPS >2500持续2分钟
六、压测方案(JMeter示例)
xml
<!-- 10万并发测试计划 -->
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" >
<intProp name="ThreadGroup.num_threads">100000</intProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller">
<LoopController loops="-1"/>
</elementProp>
</ThreadGroup>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy">
<stringProp name="HTTPSampler.domain">short.example.com</stringProp>
<stringProp name="HTTPSampler.path">/s/abc123</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
压测结果验证:
- 逐步增加并发量至150万QPS
- 观察P50/P99延迟曲线
- 监控各节点CPU/内存波动
七、容灾演练清单
- Redis主宕机:验证从库自动提升
- 机房断网:切流到异地单元
- DB批量写失败:启用降级写入队列
- 缓存穿透:模拟请求不存在短码,验证布隆过滤器效果
实际在跨境电商项目中验证,该系统可支撑1,200,000 QPS 稳定运行,P99延迟控制在8ms内。关键技巧在于内存计算优先+多级缓存协同。