Skip to content

系统设计:短网址设计(08)

Published: at 13:18:50

在这一章中,我们将解决一个有趣而经典的系统设计面试问题:设计一个像tinyurl一样的URL缩短服务。

第1步:了解问题并确定设计范围

系统设计的面试问题是故意留有余地的。为了设计一个精心设计的系统,关键是要问清楚问题。

候选人:你能举个例子说明URL缩短器的工作原理吗?

面试官:假设 URL https://www.systeminterview.com/q=chatsystem&c=loggedin&v=v3&l=long 是原始 URL。你的服务创建了一个长度较短的别名:https://tinyurl.com/y7keocwj。如果你单击较短的别名URL,它会将你重定向到原始 URL。

候选人:流量是多少?

面试官:每天产生1亿个URL。

候选人:缩短后的URL有多长?

面试官:越短越好。

候选人:缩短的网址中允许使用哪些字符?

面试官:短网址可以是数字(0-9)和字符(a-z,A-Z)的组合。

候选人:缩短的URL可以删除或更新吗?

面试官:为了简单起见,我们假设缩短的URL不能被删除或更新。

以下是基本用例:

  1. URL缩短:给定一个长的URL => 返回一个短得多的URL
  2. URL重定向:给定一个短的URL => 重定向到原来的URL
  3. 高可用性、可扩展性和容错考虑

粗略估计系统量级

重要的是,你要与面试官一起探讨假设和计算,以便你们两个人都在同一起跑线上。

第2步:提出高层次的设计方案并获得认同

在本节中,我们将讨论 API 端点、URL 重定向和 URL 缩短。

API 端点

API端点促进了客户`和服务器之间的通信。我们将设计REST风格的API。如果你不熟悉restful API,你可以查阅外部资料,比如参考资料中的资料[1]。一个URL缩短器主要需要两个API端点。

  1. 网址缩短。 要创建一个新的短 URL,客户端发送一个 POST 请求,其中包含一个参数:原始长 URL。 API 如下所示:

    POST api/v1/data/shorten

    • 请求参数:{longUrl: longURLString}。
    • 返回 shortUR
  2. URL重定向。为了将一个短的URL重定向到相应的长的URL,一个客户端发送一个GET请求。该API看起来像这样:

    GET api/v1/shortUrl

    • 返回用于HTTP重定向的`longURL

URL 重定向

图8-1显示了当你在浏览器上输入一个tinyurl时会发生什么。一旦服务器收到tinyurl请求,它就会用301重定向将短网址改为长网址。

客户端和服务器之间的详细通信情况如图8-2所示。

值得在这里讨论的一件事是 301 重定向与 302 重定向。

每种重定向方法都有其优点和缺点。如果优先考虑减少服务器负载,使用301重定向是有意义的,因为只有同一URL的第一个请求被发送到URL缩短服务器。然而,如果分析是重要的,302重定向是一个更好的选择,因为它可以更容易地跟踪点击率和点击的来源。

实现URL重定向的最直观的方法是使用哈希表。假设哈希表存储<shortURL, longURL>对,URL重定向可以通过以下方式实现。

缩短网址

让我们假设短的URL看起来像这样:www.tinyurl.com/{hashValue}。为了支持缩短URL的用例,我们必须找到一个哈希函数fx,将长URL映射到hashValue,如图8-3所示。

哈希函数必须满足以下要求。

哈希函数的详细设计将深入讨论。

第3步:深入设计

到目前为止,我们已经讨论了URL缩短和URL重定向的高层设计。在本节中,我们将深入探讨以下内容:数据模型、哈希函数、URL缩短和URL重定向。

数据模型

在高层设计中,所有的东西都存储在一个哈希表中。这是一个很好的起点;然而,这种方法在现实世界的系统中是不可行的,因为内存资源是有限的和昂贵的。一个更好的选择是将<shortURL, longURL>映射存储在一个关系数据库中。图8-4显示了一个简单的数据库表设计。简化版的表包含3列:id、shortURL、longURL。

哈希函数

哈希函数用于将一个长的URL哈希成一个短的URL,也称为hashValue。

哈希值的长度

hashValue由[0-9, a-z, A-Z]中的字符组成,包含10+26+26=62个可能的字符。要计算hashValue的长度,请找出最小的n,使62^n≥365亿。根据估计,系统必须支持多达 3650 亿个 URL。 表 8-1 显示了 hashValue 的长度和它可以支持的相应的最大 URL 数。

当n = 7时,62 ^ n = ~3.5万亿,3.5万亿足以容纳3650亿个URL,所以hashValue的长度为7。

我们将探讨两种类型的URL缩短器的哈希函数。第一种是 “哈希+碰撞解决”,第二种是 “base 62 转换”。让我们逐一来看看它们。

哈希+碰撞解决

为了缩短长的URL,我们应该实现一个散列函数,将长的URL散列成一个7个字符的字符串。一个直接的解决方案是使用知名的哈希函数,如CRC32、MD5或SHA-1。下表比较了在这个URL(https://en.wikipedia.org/wiki/Systems_design)上应用不同哈希函数后的哈希结果:

如表8-2所示,即使是最短的哈希值(来自CRC32)也太长了(超过7个字符)。我们怎样才能使它更短呢?

第一种方法是收集哈希值的前7个字符;然而,这种方法会导致哈希碰撞。为了解决哈希碰撞,我们可以递归地追加一个新的预定义字符串,直到不再发现碰撞。这一过程在图8-5中得到了解释。

这种方法可以消除碰撞;但是,查询数据库以检查每个请求是否存在短网址的成本很高。一种叫做Bloom过滤器的技术[2]可以提高性能。布隆过滤器是一种空间效率高的概率技术,用来测试一个元素是否是一个集合的成员。更多细节请参考参考资料[2]。

base 62 转换

Base 转换是 URL 缩短器常用的另一种方法。 Base 转换有助于在不同的数字表示系统之间转换成相同的数字。 使用 Base 62 转换,因为 hashValue 有 62 个可能的字符。 让我们用一个例子来解释转换的工作原理:将 $11157_{10}$ 转换为 base 62 表示($11157_{10}$ 在 base 10 系统中表示 11157)。

两种方法的比较

下表显示了两种方法的差异。

哈希+碰撞解决base 62 转换
固定短URL长度短URL长度不固定,它随着 id 变化
不需要唯一ID生成器该选项依赖于唯一ID生成器
可能出现冲突,必须解决碰撞是不可能的,因为 ID 是唯一的
不可能计算出下一个可用的短网址,因为它不依赖于ID。如果新条目的 ID 递增 1,则很容易找出下一个可用的短 URL。 这可能是一个安全问题。

URL 缩短的深入研究

作为系统的核心部分之一,我们希望URL缩短的流程在逻辑上是简单和实用的。在我们的设计中使用了62进制转换。我们建立了以下图表(图8-7)来演示这个流程。

  1. longURL 是输入的
  2. 系统检查 longURL 是否存在于数据库中
  3. 如果是的话,这意味着longURL之前被转换为shortURL。在这种情况下,从数据库中获取shortURL并将其返回给客户端。
  4. 如果不是,longURL是新的。一个新的唯一ID(主键)由唯一ID生成器生成。
  5. 使用base 62转换将ID转换为shortURL。
  6. 用ID、shortURL和longURL创建一个新的数据库记录。

为了使流程更容易理解,让我们看一个具体的例子。

值得一提的是分布式唯一 ID 生成器。 它的主要功能是生成全局唯一 ID,用于创建 shortURL。 在高度分布式的环境中,实现唯一 ID 生成器具有挑战性。 幸运的是,我们已经在“第 7 章:在分布式系统中设计唯一 ID 生成器”中讨论了一些解决方案。 你可以回过头来回顾它来刷新你的记忆。

URL重定向的深入研究

图 8-8 显示了 URL 重定向的详细设计。 由于读取多于写入,<shortURL, longURL> 映射存储在缓存中以提高性能。

URL重定向的流程总结如下:

第4步:总结

在本章中,我们讨论了 API 设计、数据模型、哈希函数、URL 缩短和 URL 重定向。

如果在面试结束时有多余的时间,这里有几个额外的谈话要点:

恭喜你走到了这一步!现在给自己一个鼓励,干得漂亮!

参考资料

[1] A RESTful Tutorial: https://www.restapitutorial.com/index.html

[2] Bloom filter: https://en.wikipedia.org/wiki/Bloom_filter