前言

凌晨两点,监控大屏突然飙红——短链服务QPS突破80万!

数据库连接池告急,Redis集群响应延迟突破500ms。

这不是演习,而是某电商平台大促的真实场景。

当每秒百万级请求涌向你的短链服务,你该如何设计系统?

今天这篇文章跟大家一起聊聊100万QPS短链系统要如何设计?

希望对你会有所帮助。

1 短链系统的核心挑战

首先我们一起看看设计一个高并发的短链系统,会遇到哪些核心的挑战。

如下图所示:

百万QPS下的三大生死关:

  1. ID生成瓶颈:传统数据库自增ID撑不住百万并发
  2. 跳转性能黑洞:302重定向的TCP连接成本
  3. 缓存雪崩风险:热点短链瞬间击穿Redis

2 短链生成

2.1 发号器的设计

发号器是短链系统的发动机。

方案对比:

方案 吞吐量 缺点 适用场景
UUID 5万/s 长度长,无法排序 小型系统
Redis自增ID 8万/s 依赖缓存持久化 中型系统
Snowflake 12万/s 时钟回拨问题 中大型系统
分段发号 50万/s 需要预分配 超大型系统

分段发号器实现(Java版):

public class SegmentIDGen {
private final AtomicLong currentId = new AtomicLong(0);
private volatile long maxId;
private final ExecutorService loader = Executors.newSingleThreadExecutor(); public void init() {
loadSegment();
loader.submit(this::daemonLoad);
} private void loadSegment() {
// 从DB获取号段:SELECT max_id FROM alloc WHERE biz_tag='short_url'
this.maxId = dbMaxId + 10000; // 每次取1万个号
currentId.set(dbMaxId);
} private void daemonLoad() {
while (currentId.get() > maxId * 0.8) {
loadSegment(); // 号段使用80%时异步加载
}
} public long nextId() {
if (currentId.get() >= maxId) throw new BusyException();
return currentId.incrementAndGet();
}
}

关键优化

  1. 双Buffer异步加载(避免加载阻塞)
  2. 监控号段使用率(动态调整步长)
  3. 多实例分段隔离(biz_tag区分业务)

2.2 短链映射算法

短码映射将长ID转换成62进制的字符串。

转换原理:

2000000000 = 2×62^4 + 17×62^3 + 35×62^2 + 10×62 + 8
= "Cdz9a"

原始ID: 2000000000,转换为62进制的值为Cdz9a。

// Base62编码(0-9a-zA-Z)
public class Base62Encoder {
private static final String BASE62 =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; public static String encode(long id) {
StringBuilder sb = new StringBuilder();
while (id > 0) {
sb.append(BASE62.charAt((int)(id % 62)));
id /= 62;
}
return sb.reverse().toString();
} // 测试:生成8位短码
public static void main(String[] args) {
long id = 1_000_000_000L;
System.out.println(encode(id)); // 输出:BFp3qQ
}
}

编码优势:

  • 6位短码可表示 62^6 ≈ 568亿种组合
  • 8位短码可表示 62^8 ≈ 218万亿种组合
  • 无意义字符串避免被猜测

3 存储架构

3.1 数据存储模型设计

3.2 缓存层级设计

3.3 缓存击穿解决方案

// Redis缓存击穿防护
public String getLongUrl(String shortCode) {
// 1. 布隆过滤器预检
if (!bloomFilter.mightContain(shortCode)) {
return null;
} // 2. 查Redis
String cacheKey = "url:" + shortCode;
String longUrl = redis.get(cacheKey);
if (longUrl != null) {
return longUrl;
} // 3. 获取分布式锁
String lockKey = "lock:" + shortCode;
if (redis.setnx(lockKey, "1", 10)) { // 10秒超时
try {
// 4. 二次检查缓存
longUrl = redis.get(cacheKey);
if (longUrl != null) return longUrl; // 5. 查数据库
longUrl = db.queryLongUrl(shortCode);
if (longUrl != null) {
// 6. 回填Redis
redis.setex(cacheKey, 3600, longUrl);
}
return longUrl;
} finally {
redis.del(lockKey);
}
} else {
// 7. 等待重试
Thread.sleep(50);
return getLongUrl(shortCode);
}
}

防护要点:

  • 布隆过滤器拦截非法短码
  • 分布式锁防止缓存击穿
  • 双重检查减少DB压力
  • 指数退避重试策略

4 跳转优化

4.1 Nginx层直接跳转

server {
listen 80;
server_name s.domain.com; location ~ ^/([a-zA-Z0-9]{6,8})$ {
set $short_code $1; # 查询Redis
redis_pass redis_cluster;
redis_query GET url:$short_code; # 命中则直接302跳转
if ($redis_value != "") {
add_header Cache-Control "private, max-age=86400";
return 302 $redis_value;
} # 未命中转发到后端
proxy_pass http://backend;
}
}

性能收益:

  • 跳转延迟从100ms降至5ms
  • 节省后端服务器资源
  • 支持百万级并发连接

4.2 连接池优化

连接池优化可以用Netty实现:

// Netty HTTP连接池配置
public class HttpConnectionPool {
private final EventLoopGroup group = new NioEventLoopGroup();
private final Bootstrap bootstrap = new Bootstrap(); public HttpConnectionPool() {
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new HttpClientInitializer());
} public Channel getChannel(String host, int port) throws InterruptedException {
return bootstrap.connect(host, port).sync().channel();
} // 使用示例
public void redirect(ChannelHandlerContext ctx, String longUrl) {
Channel channel = getChannel("target.com", 80);
channel.writeAndFlush(new DefaultFullHttpRequest(
HttpVersion.HTTP_1_1,
HttpMethod.GET,
longUrl
));
// 处理响应...
}
}

优化效果:

  • TCP连接复用率提升10倍
  • 减少80%的TCP握手开销
  • QPS承载能力提升3倍

5 百万QPS整体架构

百万QPS整体架构如下图所示:

核心组件解析:

  1. 接入层

    • CDN:缓存静态资源
    • Nginx:处理302跳转,本地缓存热点数据
  2. 缓存层

    • Redis集群:缓存短链映射
    • 布隆过滤器:拦截非法请求
  3. 服务层

    • 短链生成:分布式ID服务
    • 映射查询:高并发查询服务
  4. 存储层

    • MySQL:分库分表存储映射关系
    • TiKV:分布式KV存储ID生成状态

6 容灾设计

6.1 限流熔断策略

基于Sentinel的熔断降级:

public class RedirectController {
@GetMapping("/{shortCode}")
@SentinelResource(
value = "redirectService",
fallback = "fallbackRedirect",
blockHandler = "blockRedirect"
)
public ResponseEntity redirect(@PathVariable String shortCode) {
// 跳转逻辑...
} // 熔断降级方法
public ResponseEntity fallbackRedirect(String shortCode, Throwable ex) {
return ResponseEntity.status(503)
.body("服务暂时不可用");
} // 限流处理方法
public ResponseEntity blockRedirect(String shortCode, BlockException ex) {
return ResponseEntity.status(429)
.body("请求过于频繁");
}
}

6.2 多级降级方案

使用多级降级方案:

保证服务的高可用。

6.3 数据分片策略

基于短码分库分表:

public int determineDbShard(String shortCode) {
// 取短码首字母的ASCII值
int ascii = (int) shortCode.charAt(0);
// 分16个库
return ascii % 16;
} public int determineTableShard(String shortCode) {
// 取短码的CRC32值
CRC32 crc32 = new CRC32();
crc32.update(shortCode.getBytes());
// 每库1024张表
return (int) (crc32.getValue() % 1024);
}

这里成了16个库,每个库有1024张表。

7 性能压测数据对比

优化点 优化前QPS 优化后QPS 提升倍数
原始方案 12,000 - 1x
+Redis缓存 120,000 10x
+Nginx直跳 350,000 2.9x
+连接池优化 780,000 2.2x
+布隆过滤器 1,200,000 1.5x

压测环境:32核64G服务器 × 10台,千兆内网

总结

百万QPS短链架构核心要点如图所示:

四大设计原则:

  1. 无状态设计:跳转服务完全无状态,支持无限扩展
  2. 读多写少优化:将读性能压榨到极致
  3. 分而治之:数据分片,流量分散
  4. 柔性可用:宁可部分降级,不可全线崩溃

真正的架构艺术不在于复杂,而在于在百万QPS洪流中,用最简单的路径解决问题。当你的系统能在流量风暴中优雅舞蹈,才是架构师的巅峰时刻。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

本文收录于我的技术网站:http://www.susan.net.cn

100万QPS短链系统如何设计?的更多相关文章

  1. 阿里云云盾抗下全球最大DDoS攻击(5亿次请求,95万QPS HTTPS CC攻击) ,阿里百万级QPS资源调度系统,一般的服务器qps多少? QPS/TPS/并发量/系统吞吐量

    阿里云云盾抗下全球最大DDoS攻击(5亿次请求,95万QPS HTTPS CC攻击) 作者:用户 来源:互联网 时间:2016-03-30 13:32:40 安全流量事件https互联网资源 摘要:  ...

  2. 万级TPS亿级流水-中台账户系统架构设计

    万级TPS亿级流水-中台账户系统架构设计 标签:高并发 万级TPS 亿级流水 账户系统 背景 业务模型 应用层设计 数据层设计 日切对账 背景 我们需要给所有前台业务提供统一的账户系统,用来支撑所有前 ...

  3. 知乎技术分享:从单机到2000万QPS并发的Redis高性能缓存实践之路

    本文来自知乎官方技术团队的“知乎技术专栏”,感谢原作者陈鹏的无私分享. 1.引言 知乎存储平台团队基于开源Redis 组件打造的知乎 Redis 平台,经过不断的研发迭代,目前已经形成了一整套完整自动 ...

  4. 阿里P8架构师谈:阿里双11秒杀系统如何设计?

    秒杀是电商业务里的标志性事件,这样的典型高并发场景会遇见什么样的挑战呢,然后又是如何来解决的呢? 秒杀活动场景 淘宝双11秒杀场景,大量的用户短时间内涌入,瞬间流量巨大(高并发),比如:1000万人同 ...

  5. 转: 透过CAT,来看分布式实时监控系统的设计与实现

    评注: 开源的分布式监控系统 转:http://www.infoq.com/cn/articles/distributed-real-time-monitoring-and-control-syste ...

  6. Stackful 协程库 libgo(单机100万协程)

    libgo 是一个使用 C++ 编写的协作式调度的stackful协程库, 同时也是一个强大的并行编程库. 设计之初是为高并发分布式Linux服务端程序开发提供底层框架支持,可以让链接进程序的同步的第 ...

  7. 透过CAT,来看分布式实时监控系统的设计与实现

    2011年底,我加入大众点评网,出于很偶然的机会,决定开发CAT,为各个业务线打造分布式实时监控系统,CAT的核心概念源自eBay闭源系统CAL----eBay的几大法宝之一. 在当今互联网时代,业务 ...

  8. 基于Redis的限流系统的设计

    本文讲述基于Redis的限流系统的设计,主要会谈及限流系统中限流策略这个功能的设计:在实现方面,算法使用的是令牌桶算法来,访问Redis使用lua脚本.   1.概念 In computer netw ...

  9. Netty 100万级到亿级流量 高并发 仿微信 IM后台 开源项目实战

    目录 写在前面 亿级流量IM的应用场景 十万级 单体IM 系统 高并发分布式IM系统架构 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -10[ 博客园 总入口 ] 写在前面 ​ 大家好 ...

  10. 迄今为止最硬核的「Java8时间系统」设计原理与使用方法

    为了使本篇文章更容易让读者读懂,我特意写了上一篇<任何人都需要知道的「世界时间系统」构成原理,尤其开发人员>的科普文章.本文才是重点,绝对要读,走起! Java平台时间系统的设计方案 几乎 ...

随机推荐

  1. ARM开发板——实时获取用户点击触摸屏的LCD坐标信息(阻塞式读取)

    ARM开发板--实时获取用户点击触摸屏的LCD坐标信息(阻塞式读取) 目录 ARM开发板--实时获取用户点击触摸屏的LCD坐标信息(阻塞式读取) 1.硬件信息 2.代码需求 3.代码实现 1.硬件信息 ...

  2. 《机器人SLAM导航核心技术与实战》第1季:第3章_OpenCV图像处理

    <机器人SLAM导航核心技术与实战>第1季:第3章_OpenCV图像处理 视频讲解 [第1季]3.第3章_OpenCV图像处理-视频讲解 [第1季]3.1.第3章_OpenCV图像处理_认 ...

  3. 离线版nrfutil工具安装方法

    简介 nrfutil是Nordic提供的命令行工具集.支持以下功能: 基于Jlink的固件烧录.读取.flash擦除.recover 基于MCUBOOT的固件升级(DFU) 基于nRF5 bootlo ...

  4. 使用Python解析求解扩散方程

    引言 大家好!今天我们来探讨一个非常重要的物理问题-扩散方程,并用 Python 来求解它.扩散现象广泛存在于自然界中,从气体.液体中分子的扩散,到热量的传递,甚至污染物的扩散,都是扩散方程的应用场景 ...

  5. 一文搞懂Dockerfile

    Dockerfile官网 https://docs.docker.com/reference/dockerfile/ 什么是Dockerfile? Dockerfile 是一个文本文件,其内包含了一条 ...

  6. Selenium自动安装并引用浏览器驱动的方法

    以往的程序是先下载和把浏览器驱动放在指定目录,然后在基本中引用驱动完成整个浏览器环境配置 刚好在网上看到一个更加简便的方法,记录下来 1.先安装第三方库webdriver_manager,pip in ...

  7. 关于:js怎么获取元素的自定义属性的问题(原生JavaScript)

    最近项目需要把后端传过来的数据隐藏的保存在页面中,方便后边做事件处理时使用.鉴于之前总是在后端处理后的页面中看到元素里除了常见的id.name属性外的data-xxx,就想到:元素的属性必然是可以自定 ...

  8. GBDT算法原理及Python实现

    一.概述   GBDT(Gradient Boosting Decision Tree,梯度提升决策树)是集成学习中提升(Boosting)方法的典型代表.它以决策树(通常是 CART 树,即分类回归 ...

  9. Java对象相等判断

    你是谁啊?你是不是我??(⊙_⊙)? 我们知道比较对象相等可以使用equal方法(来自Object对象的方法) 但是你打开Object的equal方法你会发现: public boolean equa ...

  10. 工具 | WExploit

    0x00 简介 WExploit是一款基于java开发的漏洞检测工具. 下载地址 WExploit下载:WExploit下载 0x01 功能说明 Struts2 ThinkPHP 海康威视 泛微OA ...