前言


本文来看,如何设计一个缩短URL的服务,比如TinyURL. 此服务将为长URL提供短的alias别名,重定向到原始的URL。类似服务:bit.ly、goo.gl、qlink.me 等。(难度级别:简单)

接下来我们应用之前了解的基本步骤来进行设计,可回顾Grokking the System Design - 总览。

Step 1: Requirements clarifications


在澄清需求之前,先来了解下为什么需要URL 缩短功能。

URL 缩短用于为长 URL 创建较短的别名。我们将这些缩短的别名称为“短链接”。当用户点击这些短链接时,他们会被重定向到原始 URL。短链接节省了很多显示、打印、发送消息或推文时的空间。

比如说,对于一个很长的URL,类似:

https://www.educative.io/collection/page/5668639101419520/5649050225344512/5668600916475904/

使用TinyURL之后,得到:

http://tinyurl.com/jlg8zpc

URL 缩短用于优化跨设备的链接、跟踪单个链接以分析受众和活动绩效,以及隐藏附属的原始 URL。

接下来看看这个URL shortening系统的需求与目标澄清。

You should always clarify requirements at the beginning of the interview. Be sure to ask questions to find the exact scope of the system that the interviewer has in mind.

URL shortening系统需要满足如下需求:

  • Functional requirements

    • 对于给定一个URL,这个系统需要为它生成一个短且唯一的别名alias,称作短链接。

    • 当用户点击这个短链接,这个服务应该能够为它重定向到原始链接。

    • 用户可以有选择地去自定义他们URL的短链接。

    • 这些短链接在某个默认是时间段之后就会失效,用户应该能够去设置这个失效时间。

  • Non-Functional requirements

    • 系统应该是高度可用的。因为如果我们的服务关闭,所有 URL 重定向都将开始失败。

    • URL 重定向应该以最小的延迟实时发生。

    • 短链接不应该是可猜测的(也就是说有一定规律被预测到的)。

  • Extended requirements

    • 分析;例如,重定向会发生多少次?

    • 其他服务也应该可以通过 REST API 来访问我们的服务。

Step 2: System interface definition


System APIs:这一个步骤也可以在第三步之后去做。

Once we've finalized the requirements, it's always a good idea to define the system APIs. This should explicitly state what is expected from the system

我们可以使用 SOAP 或 REST API 来公开我们服务的功能。以下可用于创建和删除 URL 的 API 的定义:

createURL(api_dev_key, original_url, custom_alias=None, user_name=None, expire_date=None)deleteURL(api_dev_key, url_key)

我们如何发现和防止滥用?

恶意用户可以通过用完当前系统的所有 URL使我们破产。为了防止滥用,我们可以通过他们的 api_dev_key 限制用户。每个 api_dev_key 可以限制为每个时间段内特定数量的 URL 创建和重定向(每个开发者密钥可以设置为不同的持续时间)。

  Step 3: Back-of-the-envelope estimation


Capacity Estimation and Constraints

我们的系统将是重读的(query请求会更多)。与创建短链接相比,系统会有更多的重定向请求。让我们假设读写之间的比率为 100:1。

下面我们来进行以下几个方面的预估。

  • Traffic estimates

  • Storage estimates

  • Bandwidth estimates

  • Memory estimates

  • High level estimates

Traffic estimates

假设我们每月进行500Million (5亿) 的创建短 URL ,以 100:1 的读/写比率,我们可以预期在同一时期有 50Billion 的重定向:

100 * 500M => 50B

那么这个系统的QPS是多少?每秒创建的短链接个数:

500 million / (30 days * 24 hours * 3600seconds)  = ~ 200URLs/s

考虑到100:1的读/写比例,URLs的重定向则是:

100 * 200 URLs/s = 20K /s

Storage estimates

假设我们将每个 URL 缩短请求(和相关的短链接)存储 5 年。由于我们预计每个月有 5 亿个新 URL,我们预计存储的对象总数将是 300 亿:

500 million * 5 years * 12 months = 30 billion

让我们假设每个存储的对象大约有 500 个字节(只是一个大概的估计——我们稍后会深入研究)。我们将需要 15TB 的总存储空间:

30 billion * 500 bytes = 15 TB

Bandwidth estimates

对于写请求,因为我们预计每秒有 200 个新 URL,所以总共传入数据将是每秒 100KB:

200 * 500 bytes = 100 KB/s

对于读取请求,由于我们预计每秒有大约 20K URL 重定向,因此我们服务的总传出数据将是每秒 10MB:

20K * 500 bytes = ~10 MB/s

Memory estimates

如果我们想缓存一些经常访问的热门 URL,我们需要多少内存来存储它们?如果我们遵循 80-20 规则,即 20% 的 URL 产生 80% 的流量,我们希望缓存这 20% 的热门 URLs

由于我们每秒有 2 万个请求,我们每天将收到 17 亿个请求:

20K * 3600 seconds * 24 hours = ~1.7 billion

要缓存 20% 的这些请求,我们需要 170GB 的内存:

0.2 * 1.7 billion * 500 bytes = ~170GB

High level estimates

假设每月有 5 亿个新 URL 和 100:1 的读写比率,以下是我们服务的high level估算:

  • New URLs 200/s

  • URL redirections 20K/s

  • Incoming data 100KB/s

  • Outgoing data 10MB/s

  • Storage for 5 years 15TB

  • Memory for cache 170GB

Step 4: Defining data model


Database Design

Defining the DB schema in the early stages of the interview would help to understand the data flow among various components and later would guide towards data partitioning.

关于我们将要存储的数据的一些特点:

  • 需要存储数十亿条记录。

  • 存储的每个对象都很小(小于1K)。

  • 记录之间没有关系——除了存储哪个用户创建了一个 URL。

  • 服务的阅读量大

Database Schema:

我们需要两张表:一张用于存储有关 URL 映射的信息(URL info),另一张用于创建短链接的用户数据(User info)。

我们应该使用什么样的数据库? 

由于我们预计存储数十亿行,并且我们不需要使用对象之间的关系——像 DynamoDB、Cassandra 或

Riak 是一个更好的选择。 选择NoSQL也更容易扩展。

Step 5: High-level design


下图是一个high level的系统设计。

Basic System Design and Algorithm

我们在这里解决的问题是,如何为给定的 URL 生成一个简短且唯一的密钥。在第 1 节的 TinyURL 示例中,缩短的 URL 是“http://tinyurl.com/jlg8zpc”。这个 URL 的最后六个字符是我们要生成的短键。我们将在这里探索两种解决方案:

Encoding actual URL

Generating keys offline

我们需要思考:现有解决方案有哪些不同的问题?对应的问题有哪些workaround方法?

在这里先不赘述,之后有机会再讨论下。

Step 6: Detailed design


下图是对本系统的一个详细设计。

Data Partitioning and Replication

为了扩展我们的数据库,我们需要对其进行分区,以便它可以存储有关数十亿个 URL 的信息。我们需要提出一个分区方案,将我们的数据划分并存储到不同的数据库服务器。

Range Based Partitioning: 我们可以根据 URL 的第一个字母或哈希键将 URL 存储在单独的分区中。因此,我们将所有以字母“A”开头的 URL 保存在一个分区中,将那些以字母“B”开头的 URL 保存在另一个分区中,依此类推。这种方法称为基于范围的分区。我们甚至可以将某些不太频繁出现的字母组合到一个数据库分区中。我们应该提出一个静态分区方案,以便我们始终可以以可预测的方式存储/查找文件.

Hash-Based Partitioning: 这个方案中,我们获取我们正在存储的对象的哈希值。然后我们

根据哈希计算要使用的分区。在我们的例子中,我们可以使用“键”或实际 URL 的哈希值来确定我们存储数据对象的分区。这种方法仍然会导致分区过载,可以通过使用一致性哈希来解决.

Cache

我们可以缓存经常访问的 URL。我们可以使用一些现成的解决方案,比如 Memcache,它可以存储完整的 URL 及其各自的哈希值。应用服务器在访问后端存储之前,可以快速检查缓存是否具有所需的 URL。

我们应该有多少缓存?

我们可以从每天 20% 的流量开始,根据客户的使用模式,我们可以调整我们需要多少缓存服务器。如上估计,我们需要 170GB 内存来缓存 20% 的每日流量。由于现代服务器可以拥有 256GB 内存,我们可以轻松地将所有缓存放入一台机器中。或者,我们可以使用几个较小的服务器来存储所有这些热门 URL。

哪种缓存驱逐策略最适合我们的需求?

当缓存已满,并且我们想用更新/更热的 URL 替换链接时,我们将如何选择?最近最少使用 (LRU) 可能是我们系统的合理策略。据此,我们会首先丢弃最近最少使用的 URL。我们可以使用 Linked Hash Map 或类似的数据结构来存储我们的 URL 和哈希,这也将跟踪最近访问过的 URL。

为了进一步提高效率,我们可以复制我们的缓存服务器以在它们之间分配负载。

如何更新每个缓存副本?

每当缓存未命中时,我们的服务器就会访问后端数据库。每当发生这种情况时,我们都可以更新缓存并将新条目传递给所有缓存副本。每个副本都可以通过添加新条目来更新其缓存。如果副本已经有该条目,就可以简单地忽略它。

Telemetry

短 URL 被使用了多少次,用户的位置是什么,等等?我们将如何存储这些统计数据?如果它是在每个视图上更新的 DB 行的一部分,当一个流行的 URL 被大量并发请求猛击时会发生什么?

还有一些值得跟踪的统计数据:访问者所在的国家/地区、访问日期和时间、指向点击的网页、浏览器或访问页面的平台。

Security and Permissions

用户能否创建私有 URL 或允许一组特定用户访问 URL?

我们可以在数据库中存储每个 URL 的权限级别(公共/私有)。我们还可以创建一个单独的表来存储有权查看特定 URL 的 UserID。如果用户没有权限并尝试访问 URL,我们可以发回错误 (HTTP 401)。鉴于我们将数据存储在像 Cassandra 这样的 NoSQL 宽列数据库中,表存储权限的关键将是“哈希”(或 KGS 生成的“密钥”)。这些列将存储那些有权查看 URL 的User ID.

Step 7: Identifying and resolving bottlenecks


Load Balancer (LB)

我们可以在系统的三个地方添加负载均衡层:

1. Between Clients and Application servers

2. Between Application Servers and database servers

3. Between Application Servers and Cache servers

最初,我们可以使用简单的循环方法,在后端服务器之间平均分配传入请求。这个 LB 实现简单,不会引入任何开销。这种方法的另一个好处是,如果服务器死了,LB 将把它从轮换中取出,并停止向它发送任何流量.

Round Robin LB 的一个问题是没有考虑服务器负载。如果服务器过载或速度慢,LB 将不会停止向该服务器发送新请求。为了解决这个问题,可以放置一个更智能的 LB 解决方案,它会定期向后端服务器查询其负载并根据该负载调整流量。

Purging or DB cleanup

条目应该永远存在还是应该被清除?如果达到用户指定的过期时间,链接会发生什么?

如果我们选择主动搜索过期链接来删除它们,这会给我们的数据库带来很大的压力。相反,我们可以慢慢删除过期链接并进行惰性清理。我们的服务会确保只删除过期的链接,虽然有些过期的链接可以存活更长的时间但永远不会退回给用户。

总结


这是系统设计里的第一个实例,如果是初次接触系统设计,可能会感慨,要考虑的东西怎么那么多呀?

但这里的内容看起来多,其实是有条理逻辑的。大家只要记住系统设计的基本步骤,在每一个步骤的思考里,要把问题想全面,那整个设计图就可以构建了,并且我们在做系统设计的实际项目肯定是不停迭代,选择更优方案。

通过这篇文章,希望大家形成一个思维就是,先澄清系统的需求,目标,限制。然后再去考虑high level和detail的设计。

【Grokking the System Design】- 设计一个 TinyURL服务相关推荐

  1. [基础题]4、设计一个家政服务规范: 洗衣服, 扫地, 买菜, 做饭

    /*4.设计一个家政服务规范: 洗衣服do the Laundry, 扫地Sweep, 买菜Food , 做饭CookMeal  设计一个保姆类 Nanny , 保姆需要遵循这些规范 需求:在测试类中 ...

  2. 如何设计一个短链服务?

    相信很多小伙伴都使用过短链服务,但如果让你实现一个短链服务,你知道怎么实现吗?其实实现短链服务并不是很难,最主要还是需要知道一些设计思路,还需要有一些基础技术知识,例如:哈希算法.全局发号器等. 短链 ...

  3. 获取网关_阿里二面问了这道题:如何设计一个微服务网关系统

    有一天隔壁组的小王灰头土脸的跑过来,问我说:"李哥,你会设计微服务网关系统吗?".我一愣,小王怎么突然问这么抽象的问题,关键是我们最近也没有这样的需求.吃午饭的时候,在我旁敲侧击的 ...

  4. 如何设计一个秒杀服务

    什么是秒杀? 在特殊时间点(如京东618,天猫双11)进行的大量商品促销活动,引发大量用户集中访问和下单导致系统遭到巨大压力的考验. 传统业务架构 秒杀的特点和引发的风险 短时间大量用户访问网站,网站 ...

  5. 自己做一个短链服务,设计思路分享!

    其实实现短链服务并不是很难,最主要还是需要知道一些设计思路,还需要有一些基础技术知识,例如:哈希算法.全局发号器等. 下面一起来学习如何设计一个短链服务吧! 短链的价值 网址大家都知道,很长的一串字符 ...

  6. 架构设计(7)—如何设计一个架构

    愿景已经确定架构愿景和目标. 需求分析明确架构要解决当前什么问题. 那接下来就是如何着手开始做架构设计. 一.如何开始设计一个架构:方式方法 架构不是像平常写代码一样,对就是对,错就是错,它并无对错之 ...

  7. 【技术分享】Sanic+Amis:3天快速搭建一个web服务

    前言 测试工作中,需要搭建一些生产力小工具来提高工作效率. 基于web的服务,无需终端可多人协作,易部署,成为第一选择. 问题:搭建web服务需要全栈知识,编写http服务和前端样式需要一定技术门槛. ...

  8. 【架构师必知必会系列】系统架构设计需要知道的5大精要(5 System Design fundamentals)...

    无论是在大厂还是初创公司,技术产品经理 (TPM)都需要具备系统设计的基础知识.从历史上看,系统设计基础知识通常是软件工程师在面试时的要求,而 TPM 不受此期望的约束.然而,现在趋势正在发生变化.作 ...

  9. 系统设计 (二)设计类似TinyURL的短URL服务

    让我们设计一个类似TinyURL的短URL服务.此服务将提供重定向到长URL的短URL别名.类似服务:bit.ly.goo.gl.qlink.me等. 1.为什么我们需要缩短URL? URL缩短用于为 ...

最新文章

  1. 《深入理解ElasticSearch》——2.4 批量操作
  2. 根据当前记录获取前一条与下一条记录常用 sql语句
  3. linux共享xp打印机驱动下载,给debian共享winxp下的打印机
  4. js jq 一些属性
  5. WIN版的Jenkins Master加入LINUX的SLAVE节点,并作C++程序的集成交付
  6. 都说不建议使用递归操作,到底为什么?
  7. mysql的dockerfile_dockerfile构建mysql镜像
  8. php ip重复注册,php中表单的重复提交怎么通过记录IP来防止
  9. web框架--MVC、MTV
  10. windows 下杀掉占用端口的程序
  11. 微信小程序 手写签名_【微信小程序canvas】实现小程序手写板用户签名(附代码)...
  12. HTML+CSS系列实战之表格
  13. CF1108D Diverse Garland
  14. 无需会员,百度云不限速下载!
  15. 古琴【A5】良宵引-不好听
  16. 全排列问题(AcWing 823. 排列)
  17. 【移动应用开发】2022/2023 年 8 大移动应用程序开发趋势
  18. 用python实现时间序列白噪声检验
  19. 物权法全文内容有哪些呢-广告外链_有哪些网络推广免费平台?通过哪些平台进行免费推广及注意事项?...
  20. SCI,EI收录的中国期刊

热门文章

  1. 面试题收集二(33道)
  2. 无法访问maven官网
  3. 深度Linux使用小记 (附带视频)
  4. python 魔法函数 运行时_python内置函数和魔法函数
  5. linux 实现SNAT技术
  6. 循环赛赛程表的分治法实现
  7. 两人取石子游戏 组合数学-博弈问题
  8. 穷举问题-搬砖某工地需要搬运砖块,已知男人一人搬3块,女人一人搬2块,小孩两人搬1块。如果想用n人正好搬n块砖,问有多少种搬法? 输入格式:
  9. Java高并发(八)——Thread pool 如何创建及常见并行模式
  10. 2.8.4SQLAlchemy