新浪微博的短链越来越长了。怎么办?
发布时间:
2024-09-18 22:38
阅读量:
17
速答
- 不担心
- 短链中的 [0-9,a-z,A-Z] 总共 62 种字符。只需要 7 位,就可以支持 62^7~= 3.5 兆(35000亿)个短链。如果系统运行10年,每天平均下来可以支持生成 3.5*10^12/10/365~=9.59 亿个短链,这是一个很大的数了
本文首发于 https://doc.fenglyulin.com/
Distributed URL Shortener
分布式、高性能短链生成服务的系统设计
需求
- 生成 URL 操作,写操作,每天 100M 次(每秒约 1160 次)
- 根据短链读长链,读操作,假设为写的 10 倍,即每天 1B 次(每秒约 11600 次)
- 假设
- 系统要运行 10 年,那总共生成需要
10*365*100M = 365B
个不冲突的 URL ID - 平均输入的 URL 长度是 100 bytes,那么 10 年的存储需求为
100*365B~=3650TB
系统架构和流程图
实体:用户、服务器、数据库、缓存、负载均衡器,etc。
- 生成短链
- 用户请求生成短链 API,参数是长链
- 如果缓存和数据库中有该长链记录,则返回 error_duplicate
- 系统使用哈希算法生成相应的短链,存储记录(ID,长链,短链)数据库中,也许可以更新到缓存中
- 返回短链
- 请求长链
- 用户请求短链重定向 API,参数是短链
- 如果缓存有该短链记录则返回对应长链
- 如果查询数据库有该短链记录则返回对应长链、刷新缓存,否则返回 error_not_found
- 返回长链,且 HTTP
301
或302
重定向到长链
TODO: 系统架构图、序列图
细节设计
1)接口设计
REST-Style API
- REST 中所有的内容都被视为资源,每个资源通过一个唯一的 URI
- REST API 通常使用标准的 HTTP 方法(动词)来操作资源
- 每个请求都是独立的、无状态的,这意味着服务器不会存储客户端的状态
API 设计
- 生成短链 API
POST api/v1/data/shorten
- request: long_url(string)
- response: shorten_url(string)
- 短链重定向 API
GET api/v1/shorten_url
- request: shorten_url(string) in the HTTP url
- response: long_url(string)
2)短链生成 URL shortening
需求
- 每个长链都能够映射到一个短链
- 每个短链都能够映射回长链
需要一个短链生成方法,一般使用哈希函数来完成这个事(下面详解)
- 哈希函数
hash(long_url)=shorten_url
TODO: 图
3)URL 重定位 redirect
GET api/v1/shorten_url
使用 HTTP response 的 status code 301 来进行长链的重定向
301
代表永久重定向(permanent redirecting),客户端会记住这个重定向响应,后续对短链 API 的请求会自动重定向到长链上- 优点:节约服务资源
- 缺点:短链服务器无法跟踪每次短链的请求,无法即时失效长链
302
代表暂时重定向(temporary redirecting),客户端不会记住响应,每次都会重新请求服务器获得长链。- 优点:短链服务器可以跟踪每次短链的请求,可以即时失效长链
- 缺点:消耗服务资源
4)短链生成方法(哈希方法)
常见方法
- 哈希函数(带碰撞检测)
- base62 编码
哈希函数
- 组成:
[0-9,a-z,A-Z]
总共 62 位,所以只需要生成 7 位,就可以支持62^7~= 3.5Trillion
个哈希值,超过需求的365B
个不同短链 - 生成方法:crc32, md5, sha-1 等
- 使用
- 由于生成的哈希值可能会超过 7 位,我们需要截断前 7 位哈希值
- 引入碰撞检测:由于前 7 位可能会碰撞,我们可以加上预先定好的 string 或数字,重新生成哈希
TODO:流程图
base62 编码
- 组成:
[0-9,a-z,A-Z]
总共 62 位 - 生成方法:不断模除 62,每次的余数都映射成
[0-9,a-z,A-Z]
,直到余数为 0 - 使用
- 由于 base62 接受的输入是数字,我们需要使用唯一ID生成器给每个长链生成ID(要存到数据库的id键中)
- 基于这个ID,来
base62 与 base 64 区别:
- Base64 使用 64 个字符,除了包括
A-Z
、a-z
、0-9
,还加上两个特殊字符+
和/
。编码结果有时还会在末尾加上=
作为填充 - Base64 编码效率高,Base62 效率略低
- Base64 编码将输入的二进制数据每 3 个字节(24 位)分成 4 组,每组 6 位。每个 6 位的组对应 Base64 字符集中的一个字符。不满三个字节使用
=
填充。
TODO: 流程图
哈希函数和 base 62 的对比
方法 | 哈希(碰撞检测) | base62 编码 |
---|---|---|
是否固定长度 | YES | NO |
是否需要ID生成器 | No | YES |
是否需要解决碰撞 | YES | NO |
是否可以猜出下一个短链 | NO | 取决于ID生成器 |
5)数据库设计
短链存储方式(老生常谈)
- 单机内存中的哈希表,比如 golang 的 map,python 的 dict
- 缓存、数据库
数据库表 shorten_url_tab
,有三个 columns
- id, primary key, bigint
- short_url, string
- long_url, string
- c_time, bigint (if need)
- m_time, bigint (if need)
其中 id 是否是自增(auto_increment),取决于
- 是否使用 base62 这类需要唯一 ID 生成的哈希方法
- 是否对可扩展、高性能、单点容错有需求
TODO: 数据库ER图
性能需求
重看开始的需求
- 生成 URL 操作,写操作,每天 100M 次(每秒约 1160 次)
- 根据短链读长链,读操作,假设为写的 10 倍,即每天 1B 次(每秒约 11600 次)
- 答:这两个需求可以通过服务器和数据库的垂直扩展、水平扩展,引入缓存、引入CDN、引入负债均衡达到
- 假设
- 系统要运行 10 年,那总共生成需要
10*365*100M = 365B
个不冲突的 URL ID - 已经在哈希函数中解答了:
[0-9,a-z,A-Z]
总共 62 位,所以只需要生成 7 位,就可以支持62^7~= 3.5Trillion
个哈希值,超过需求的365B
个不同短链 - 平均输入的 URL 长度是 100 bytes,那么 10 年的存储需求为
100*365B~=3650TB
- 答:考虑数据库的垂直扩展、水平扩展,基于ID sharding。引入分布式数据库等等。
Reference
- System Design Interview by Alex
- wiki
END