系统设计-如何设计一个高并发的短链系统?

41 阅读9分钟

一、 核心业务假设

短链系统的核心功能,通过将长链接转换为短字符串的方式,提升链接的可读性和传播性,适用于消息通知、广告推广、社交媒体等场景。

如下图所示:

短链系统的核心功能有两个,短链跳转和短链生成。

  1. 日生成短链数量:5 万条
  2. 短链有效期:5 年
  3. 单条短链活跃期:前 10 天
  4. 单条短链日均访问量:1000 次
  5. 访问集中特征:80% 的访问集中在白天 10 小时
  6. 单条短链存储大小:1KB(长链 + 短链 + 附属信息)
  7. 响应数据大小:1KB(长链 500B + 其余数据 500B)
  8. 响应时间(RT):80% 请求 RT≤5ms,99% 请求 RT≤20ms,平均 RT=10ms

二、 容量评估

存储容量评估

(1) 总数据量计算

总数据条数 = 日生成量 × 年天数 × 有效期= 50000 × 365 × 5= 9125 万条 (2) 总存储占用

总存储容量 = 总数据条数 × 单条存储大小= 91250000 × 1KB= 91250000 KB

(3) 单位换算

  • 按二进制标准换算(1GB = 1024MB、1MB = 1024KB):总存储容量 ≈ 91250000 ÷ 1024 ÷ 1024 ≈ 87 GB
  • 按工程简化换算(1GB ≈ 1000000 KB):总存储容量 ≈ 90 GB

(4) 存储方案建议

基于存储容量仅约 90GB,MySQL 单机即可满足存储需求。可通过按时间分表(如按月份分表)优化历史数据查询性能,同时配置定时归档策略,降低活跃表的数据量。

缓存容量评估

(1) 缓存数据范围

缓存活跃期内的短链数据(前 10 天生成的短链),覆盖核心访问流量,降低后端数据库压力。

(2) 缓存容量计算

缓存容量 = 日生成量 × 活跃天数 × 单条数据大小= 50000 × 10 × 1KB= 500 MB

(3) 缓存方案建议

采用 Redis 单机部署即可满足需求,配置过期策略(活跃期 10 天后自动淘汰),避免缓存数据堆积。

QPS 容量评估

(1) 集中时段平均 QPS 计算

核心公式:

image.png

  • 集中时段总请求数 = 短链总数 × 活跃天数 × 日均访问量 × 访问集中率= 50000 × 10 × 1000 × 80% = 4 亿次
  • 集中时段总秒数 = 10 小时 × 60 分钟 / 小时 × 60 秒 / 分钟 = 36000 秒
  • 集中时段平均 QPS = 400000000 ÷ 36000 ≈ 11111

(2) 峰值 QPS 计算

按业务峰值为平均流量 2 倍估算:峰值 QPS = 平均 QPS × 2 = 11111 × 2 = 22222工程设计容量向上取整,按 2.3 万 QPS 规划。

系统吞吐量与线程数评估

(1) 理论吞吐量计算

核心公式(基于响应时间与线程数):QPS=平均RT(ms)1000 ms​×线程数

  • 平均 RT = 10ms,单线程每秒可处理请求数 = 1000 ÷ 10 = 100 QPS
  • 支撑 2 万 QPS 所需线程数 = 20000 ÷ 100 = 200 线程
  • 80% 请求 RT≤5ms、99% 请求 RT≤20ms 的指标,可保障该吞吐量的稳定性,避免长尾请求占用线程资源。

带宽容量评估

(1) 每秒数据传输量计算

每秒数据量 = 峰值 QPS × 单请求响应数据大小= 23000 × 1KB = 23000 KB/s

(2) 单位换算

  • 换算为 MB/s(工程简化:1MB = 1000KB):每秒数据量 = 23000 KB/s ÷ 1000 = 23 MB/s
  • 换算为网络带宽单位 Mbit/s(1Byte = 8bit):所需带宽 = 23 MB/s × 8 = 184 Mbit/s

(3) 带宽方案建议

按 200 Mbit/s 带宽规划,预留约 10% 余量应对突发流量

三、核心业务逻辑

跳转原理

HTTP 301/302 的底层本质是服务端向客户端返回特定状态码 + Location 响应头,客户端(浏览器 / 爬虫)接收到后自动发起新请求到Location指定的 URL。

状态码含义核心特性典型业务场景
301永久重定向浏览器缓存重定向关系,SEO 权重转移到新 URL域名更换、页面永久迁移
302临时重定向不缓存,每次请求都走原 URL,SEO 权重保留登录跳转、临时维护、A/B 测试

核心协议交互流程(Go 服务端视角)

  1. 客户端(如浏览器)向 Go 服务端发送 HTTP 请求(GET/POST 等);

  2. Go 服务端解析请求后,构造响应:

    • 设置响应状态码为 301/302;
    • 设置Location响应头,指定新 URL;
    • 发送响应给客户端;
  3. 客户端接收到响应后,自动向Location的 URL 发起新请求。

生成原理

接下来,我们来讲讲短链生成的实现原理:长链哈希自增序列随机字符串预生成四种方案。

如下图所示:

长链哈希

(1)目前常见的哈希算法有MD5、SHA1、MurmurHash等,先将长链通过这些算法生成一个哈希值。

如:将长链“ www.example.com/very/long/u… ”进行哈希计算,得到结果“ 3a6bd3e2560a3d23eea436fcfb7e44c7 ”。

(2)将生成的结果字符串进行截断,留下前6位短码,得到结果“3a6bd3”。

目前,6位短码已经能够满足绝大多数使用场景,因为6位的短码可以提供568亿种的组合。

(3)将截取后得到的短码和原始的长链建立一一映射关系,并存储在数据库或缓存中。

这样,当用户访问短码对应的短链时,系统就可以通过查询映射关系找到原始的长链,并进行重定向。

该方案的优点是实现简单,但存在哈希碰撞的可能性,在碰撞时需要在原来的哈希值上增加随机数再进行哈希。

自增序列

(1)目前有两种主流的实现方式,通过雪花算法或数据库自增ID来生成唯一数字ID。

前者则强依赖机器时钟,如果机器上时钟回拨,会导致ID重复。

image.png

后者的ID生成涉及到数据库操作,性能相对不高,且引入数据库会导致链路变长,增加出错概率。同时数据库分片导致id重复。

(2)以进制转换的方式将数字ID进行缩短,如:十进制数字1234567890的六十二进制为1l3MoK

(3)将截取得到的短码和原始的长链建立一一映射关系,并存储在数据库或缓存中。

该方案的优点是不存在长链哈希的碰撞问题,但如上文所述,也会根据实现方式不同引入其他问题。

随机字符串

(1)可以通过取UUID前8位字符的方式生成短码,将实现起来非常简单。

import java.util.UUID;
public class GenerateUUID {
    public static void main(String[] args) {
        // 生成一个随机的 UUID
        UUID uuid = UUID.randomUUID();
        // 输出生成的 UUID
        String uuidString = uuid.toString();       
        // 截取前八位
        String firstEightChars = uuidString.substring(0, 8);
        // 输出截取的前八位
        System.out.println("First eight characters: " + firstEightChars);
    }
}

(2)以进制转换的方式将数字ID进行缩短,如:十六进制数字5ca877b6的六十二进制为k3v6bO。

(3)将截取得到的短码和原始的长链建立一一映射关系,并存储在数据库或缓存中。

该方案的优点是实现简单,通过JDK自带的工具类即可实现,连jar包都不需要引入,但会存在数字ID重复的问题。

预生成

该方案主要解决上述方案中,临时生成哈希值存在哈希碰撞,或临时生成数字ID存在重复的问题。

预先生成一批短码并存储在数据库中,当需要生成短链时,直接将未使用的短码与长链接进行关联即可。

当然,该方案相比于其他三种方案,实现起来更复杂一些。

四、完整流程

第一步:生成短链

  1. 输入长链,先通过「hash + 进制转换」(比如 MD5 哈希后转 62 进制)生成对应的短链

第二步:避免短链重复(两种校验逻辑)

逻辑 1:数据库直接校验

  • 判断生成的短链在DB 中是否存在

    • 若存在(Y):给原长链加上 “冗余字符串”(避免重复),回到 “hash + 进制转换” 步骤,重新生成新短链;
    • 若不存在(N):直接将 “短链 + 对应长链” 插入数据库,流程结束。

逻辑 2:布隆过滤器前置校验

  • 先通过布隆过滤器判断短链是否存在(布隆过滤器是内存级的快速去重工具,比数据库查询更快):

    • 若不存在(N):先把短链写入布隆过滤器,再将 “短链 + 长链” 插入数据库,流程结束;
    • 若存在:会回到逻辑 1 的 “DB 中是否存在” 校验(因为布隆过滤器有小概率误判,需数据库最终确认)。

核心作用

通过 “哈希生成短链 + 去重校验(数据库 / 布隆过滤器)”,既实现了长链转短链,又避免了短链重复的问题,同时布隆过滤器的引入提升了去重的效率。