Pastebin设计之旅:从零设计网络文本存储系统

news2024/12/25 1:17:04

项目简介:Pastebin是一个在线的文本存储平台,让用户可以存储和分享代码片段或者其他类型的文本。它支持多种编程和标记语言的语法高亮,用户可以选择让他们的"paste"公开或私有。无需注册就可以使用,但注册用户可以更方便地管理他们的"paste"。Pastebin常被开发者、系统管理员以及其他技术专业人员用于分享和协作。

现在让我们来设计一个类似Pastebin的网络服务,用户可以在这里储存纯文本。用户可以输入一段文本,并获取一个随机生成的URL来访问这段文本。

类似的产品有:pastebin.com,controlc.com,hastebin.com,privatebin.net

系统难度等级:初级

1、什么是Pastebin

Pastebin及类似服务让用户能够在网络(通常指的是互联网)上存储纯文本或图像,并生成唯一的URL来访问上传的数据。这样的服务也被用来快速地在网络上共享数据,用户只需传递URL,其他用户就可以查看其内容。

如果你以前没有使用过pastebin.com,建议尝试在那里创建一个新的“Paste”,并花些时间浏览他们服务提供的不同选项。这将在理解本章时有很大帮助。

对于类似于Pastebin这样的代码或文本分享平台,中国并未有一款特别知名或广泛使用的网站。很多开发者会使用GitHub Gist来分享代码片段,此外,国内也有一些代码托管平台,比如Coding.net和Gitee,也提供代码分享和协作的功能。

2、系统的需求和目标

Pastebin服务需要满足以下要求:

功能需求

  1. 用户应能够上传或“粘贴”他们的数据,并获得一个独特的URL来访问它。
  2. 用户只能上传文本。
  3. 数据和链接将在特定的时间段后自动过期;用户应可以指定过期时间。
  4. 用户可以选择为他们的粘贴内容设置一个自定义的别名。

非功能性需求

  1. 系统必须具有高度的可靠性,上传的任何数据都不应丢失。
  2. 系统必须始终可用。这是因为如果我们的服务暂停,用户将无法访问他们的粘贴内容。
  3. 用户应能实时访问他们的粘贴内容,延迟要最小。
  4. 粘贴的链接不能被轻易猜出(不能被预测)。

扩展需求

  1. 分析,例如,一个粘贴内容被访问了多少次?
  2. 我们的服务也应该通过REST APIs供其他服务访问。

3、注意事项

Pastebin与URL缩短服务(系统设计上一个案例)有一些相同的需求,但是我们还应该考虑一些额外的设计因素。

用户每次粘贴的文本量应有何限制?我们可以限制用户不得粘贴超过 10 M B 10MB 10MB的数据,以防止服务被滥用。

我们是否应对自定义**URL**的大小设置限制?由于我们的服务支持自定义URL,用户可以选择他们喜欢的任何URL,但是提供自定义URL并非强制性的。然而,对自定义URL设置大小限制是合理的(而且通常是我们期望的),这样我们可以保持一致的URL数据库。

4、容量估计与约束

我们的服务将是读取密集型的;相比新建Paste,读取请求会更多。我们可以假设读取与写入之间的比例是 5 : 1 5:1 5:1

流量预估:Pastebin类似的服务并不预期有如微信或今日头条那样的流量,这里我们假设每天有一百万个新的粘贴内容添加到我们的系统中。这样算来,我们每天有五百万次的读取。

每秒新粘贴内容

1 M / ( 24 小时 ∗ 3600 秒 )   = 12 粘贴 / 秒 1M / (24 小时 * 3600 秒) ~= 12 粘贴/秒 1M/(24小时3600) =12粘贴/

每秒粘贴读取次数

5 M / ( 24 小时 ∗ 3600 秒 )   = 58 读取 / 秒 5M / (24 小时 * 3600 秒) ~= 58 读取/秒 5M/(24小时3600) =58读取/

存储预估:用户最多可以上传 10 M B 10MB 10MB的数据;一般来说,Pastebin类似的服务用于分享源代码、配置文件或日志。这些文本并不大,所以我们假设每个粘贴内容平均含有 10 K B 10KB 10KB

按照这个速率,我们每天将存储 10 G B 10GB 10GB的数据。

1 M ∗ 10 K B = > 10 G B / 天 1M * 10KB => 10 GB/天 1M10KB=>10GB/

如果我们想将这些数据存储十年,那我们总共需要 36 T B 36TB 36TB的存储容量。

每天有 1 M 1M 1M的粘贴内容,十年后我们将有36亿的粘贴内容。我们需要生成并存储键来唯一地标识这些粘贴内容。如果我们使用Base64编码([A-Z, a-z, 0-9, ., -]),我们将需要六个字符的字符串:

6 4 6   = 68.7 亿个唯一字符串 64^6 ~= 68.7亿个唯一字符串 646 =68.7亿个唯一字符串

如果存储一个字符需要一个字节,存储 36 亿 36亿 36亿个键所需的总大小将是:

3.6 B ∗ 6 = > 22 G B 3.6B * 6 => 22 GB 3.6B6=>22GB

相比于 36 T B 36TB 36TB 22 G B 22GB 22GB微不足道。为了保留一些余量,我们将假设一个70%的容量模型(意味着我们在任何时候都不希望使用超过总存储容量的70%),这将使我们的存储需求增加到 51.4 T B 51.4TB 51.4TB

带宽预估:对于写入请求,我们预计每秒新增12个粘贴内容,导致每秒进入 120 K B 120KB 120KB的数据。

12 ∗ 10 K B = > 120 K B / s 12 * 10KB => 120 KB/s 1210KB=>120KB/s

至于读取请求,我们预计每秒58个请求。因此,总的数据出口(发送给用户)将是 0.6 M B / s 0.6 MB/s 0.6MB/s

58 ∗ 10 K B = > 0.6 M B / s 58 * 10KB => 0.6 MB/s 5810KB=>0.6MB/s

尽管总的进出口并不大,我们在设计服务时应记住这些数字。

内存预估:我们可以缓存一些被频繁访问的热门粘贴内容。根据80-20原则,意味着20%的热门粘贴内容产生了80%的流量,我们希望将这20%的粘贴内容进行缓存。

既然我们每天有500万次的读取请求,要缓存这些请求中的20%,我们需要:

0.2 ∗ 5 M ∗ 10 K B   = 10 G B 0.2 * 5M * 10KB ~= 10 GB 0.25M10KB =10GB

因此,我们大约需要 10 G B 10GB 10GB的内存来缓存那些热门的粘贴内容。

5、系统API

我们可以有SOAPREST API来公开我们服务的功能。以下是创建/检索/删除粘贴内容的API定义:

addPaste(api_dev_key, paste_data, custom_url=None user_name=None, paste_name=None, expire_date=None)

参数

api_dev_key (字符串): 已注册帐户的API开发者密钥。这将用于基于分配的配额对用户进行限流等操作。

paste_data (字符串): 粘贴的文本数据。

custom_url (字符串): 可选的自定义URL

user_name (字符串): 可选的用于生成URL的用户名。

paste_name (字符串): 粘贴内容的可选名称。

expire_date (字符串): 粘贴内容的可选过期日期。

返回: (字符串)

成功插入返回可以访问粘贴内容的URL,否则,它将返回一个错误码。

类似地,我们可以有检索和删除粘贴内容的API:

getPaste(api_dev_key, api_paste_key) 

其中api_paste_key是一个表示要检索的粘贴内容的粘贴键的字符串。这个API将返回粘贴内容的文本数据。

deletePaste(api_dev_key, api_paste_key) 

成功删除返回true,否则返回false

6、数据库设计

关于我们存储的数据性质,我们有一些观察:

  1. 我们需要存储数十亿条记录。
  2. 我们存储的每个元数据对象都很小(小于 1 K B 1KB 1KB)。
  3. 我们存储的每个粘贴对象大小适中(可以达到几MB)。
  4. 记录之间没有关系,除非我们要存储哪个用户创建了哪个粘贴内容。
  5. 我们的服务主要是读取操作。

数据库架构

我们需要两个表,一个用于存储粘贴内容的信息,另一个用于存储用户的数据。

在这里插入图片描述

这里,URlHash是TinyURL的URL等效项,ContentKey是指向一个外部对象的引用,该对象存储粘贴内容的内容;我们将在本章后面讨论粘贴内容的外部存储。

7、顶层设计

在顶层设计上,我们需要一个应用层来处理所有的读取和写入请求。应用层将与存储层进行通信,以存储和检索数据。我们可以将存储层划分为两部分,一部分数据库存储与每个粘贴内容、用户等相关的元数据,另一部分将粘贴内容存储在某些对象存储中(如阿里云OSS)。这种数据的划分也允许我们单独进行扩展。

在这里插入图片描述

8、组件设计

A. 应用层

我们的应用层将处理所有的进出请求。应用服务器将与后端数据存储组件进行通信以服务这些请求。

如何处理写入请求?在收到写入请求后,我们的应用服务器将生成一个六位随机字符串,这将作为粘贴的键(如果用户没有提供自定义键)。然后,应用服务器将粘贴的内容和生成的键存储在数据库中。成功插入后,服务器可以将键返回给用户。这里可能存在的一个问题是由于键重复而导致插入失败。由于我们是生成一个随机键,所以新生成的键可能与现有的键相匹配。在这种情况下,我们应该重新生成一个新的键并再试一次。我们应该一直重试,直到我们不再因为重复键看到失败。如果用户提供的自定义键已经在我们的数据库中存在,我们应该向用户返回一个错误。

上述问题的另一种解决方案可能是运行一个独立的键生成服务(KGS),它提前生成随机的六位字符串,并将它们存储在数据库中(我们称之为key-DB)。每当我们想要存储一个新的粘贴,我们只需要取一个已经生成的键并使用它。这种方法将使事情变得非常简单和快速,因为我们不会担心重复或碰撞。KGS将确保所有插入key-DB的键都是唯一的。KGS可以使用两个表来存储键,一个用于尚未使用的键,一个用于所有已使用的键。一旦KGS向应用服务器提供了一些键,它可以将这些移动到已使用的键表中。KGS可以始终在内存中保持一些键,以便每当服务器需要它们时,它可以快速提供。一旦KGS在内存中加载了一些键,它就可以将它们移动到已使用的键表中;这样我们就可以确保每个服务器获取到的键都是唯一的。如果KGS在使用所有加载到内存中的键之前死掉,我们将浪费这些键。我们可以忽略这些键,因为我们有大量的键。

KGS会出现单点故障吗?会的。为了解决这个问题,我们可以有一个备用的KGS副本,每当主服务器死掉时,它可以接管来生成和提供键。

每个应用服务器可以从**key-DB**中缓存一些键吗?是的,这肯定可以加快速度。虽然在这种情况下,如果应用服务器在消费所有键之前死掉,我们将最终丢失那些键。这可能是可以接受的,因为我们有 68 B 68B 68B个唯一的六位字母键,这比我们需要的要多得多。

如何处理粘贴读取请求?在收到读取粘贴请求后,应用服务层联系数据存储。数据存储搜索键,如果找到了,就返回粘贴的内容。否则,返回一个错误码。

B. 数据存储层

我们可以将我们的数据存储层分为两部分:

  1. 元数据数据库:我们可以使用像MySQL这样的关系数据库,或者像DynamoCassandra这样的分布式Key-Value存储。
  2. 对象存储:我们可以将我们的内容存储在像阿里云OSS这样的对象存储中。每当我们感觉到我们的内容存储容量已经满了,我们可以通过平台直接扩容。

在这里插入图片描述

9、数据分区和复制

为了扩展我们的数据库,我们需要对其进行分区,以便它能存储数十亿个URL的信息。因此,我们需要设计一个分区方案,将我们的数据划分并存储到不同的数据库中。

A. 基于范围的分区:我们可以根据哈希键的第一个字母在不同的分区中存储URL。因此,我们将所有以字母“A”(和“a”)开头的URL哈希键存储在一个分区中,将以字母“B”开头的URL存储在另一个分区中,以此类推。这种方法被称为基于范围的分区。我们甚至可以将某些出现频率较低的字母组合到一个数据库分区中。因此,我们应开发一个静态分区方案,始终以可预测的方式存储/查找URL

这种方法的主要问题是可能导致数据库服务器不平衡。例如,我们决定将所有以字母“E”开头的URL放入一个数据库分区,但后来我们发现以字母“E”开头的URL过多。

B. 基于哈希的分区:在这种方案中,我们对存储的对象进行哈希。然后,我们根据哈希值计算使用哪个分区。在我们的情况下,我们可以获取key或短链接的哈希值,以确定我们存储数据对象的分区。

我们的哈希函数将随机地将URL分配到不同的分区(例如,我们的哈希函数可以始终将任何key映射到[1…256]之间的一个数字)。这个数字将表示我们存储对象的分区。

这种方法仍然可能导致分区负载过大,这可以通过使用’一致性哈希’来解决。

10、缓存

我们可以缓存频繁访问的URL。我们可以使用任何现成的解决方案,比如Memcached,它可以存储完整的URL及其对应的哈希值。因此,应用服务器在访问后端存储之前,可以快速检查缓存是否有所需的URL

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

哪种缓存驱逐策略最适合我们的需求?当缓存满了,我们想用一个新的/更热门的URL替换一个链接,我们应该如何选择?最近最少使用(LRU)可能是我们系统的一个合理策略。根据这个策略,我们首先丢弃最近最少使用的URL。我们可以使用Linked Hash Map或类似的数据结构来存储我们的URL和哈希值,这也会记录最近访问过的URL

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

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

在这里插入图片描述

11、负载均衡(LB)

我们可以在系统中的三个位置添加负载均衡层:

  • 客户端与应用服务器之间
  • 应用服务器与数据库服务器之间
  • 应用服务器与缓存服务器之间

最初,我们可以使用一个简单的轮询方法,将进入的请求均等地分配到后端服务器。这种负载均衡方法简单易行,不会引入任何额外的开销。这种方法的另一个优点是,如果一个服务器死机,负载均衡器会将其从轮询中移除,停止向其发送任何流量。

轮询负载均衡的一个问题是,我们没有考虑到服务器的负载。如果一个服务器过载或运行缓慢,负载均衡器不会停止向该服务器发送新的请求。为了处理这个问题,我们可以放置一个更优的负载均衡解决方案,它定期查询后端服务器的负载,并根据负载情况调整流量。

12、清除或数据库清理

key条目是否应永久存在,还是应该被清除?如果达到用户设定的过期时间,链接应该怎么处理?

如果我们选择持续寻找过期链接并将其删除,这将对我们的数据库产生很大压力。相反,我们可以慢慢地删除过期的链接,进行懒人清理。我们的服务将确保只删除过期的链接,尽管有些过期链接可能会存在更长时间,但永远不会被返回给用户。

  • 每当用户试图访问一个已过期的链接,我们可以删除该链接并向用户返回错误。
  • 我们可以设置一个单独的清理服务,定期从我们的存储和缓存中删除过期的链接。这个服务需要非常轻量,只在预计用户流量较低的时候运行。
  • 我们可以为每个链接设定一个默认的过期时间(例如两年)。
  • 删除过期链接后,我们可以将该Key重新放回Key-DB,以供重复使用。
  • 我们是否应删除一段时间(比如说六个月)内没有被访问过的链接?这可能有点不合适。由于存储成本越来越低,我们可以决定永久保存链接。

在这里插入图片描述

13、数据跟踪

我们如何统计短链接被使用的次数、用户的位置等信息?我们如何储存这些统计信息?如果它是数据库中的一部分,每次查看都需要更新,那么当一个流行的短链接被大量并发请求瞬间涌入时,会发生什么?

一些值得追踪的统计数据:访客的国家、访问的日期和时间、引导点击的网页、访问页面的浏览器或平台。

14、安全和权限

用户能否创建私有URL或者允许特定的用户组访问某个URL

我们可以在数据库中每个URL的条目里存储访问权限级别(公开/私有)。我们也可以创建一个单独的表来存储有权访问特定URL的用户的UserID。如果一个用户没有权限但试图访问一个URL,我们可以返回一个错误(HTTP 401)。考虑到我们的数据储存在一个类似CassandraNoSQL宽列数据库中,储存权限的表的key将是‘哈希值’(或KGS生成的key)。列将储存有权查看URL的用户的UserID

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/702148.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

森海塞尔重磅推出TC Bars智能音视频一体机, 为中小型协作空间缔造理想解决方案

森海塞尔重磅推出TC Bars智能音视频一体机, 为中小型协作空间缔造理想解决方案 全球音频行业先驱森海塞尔重磅推出首款内置摄像头的可扩展一体化会议设备 德国韦德马克,2023年6月13日——森海塞尔作为先进音频技术的首选,致力于使协作与学习…

力扣 617. 合并二叉树

题目来源: C题解1:使用队列实现层序遍历。基于root1,遇到可覆盖部分,直接将该节点指向对应节点,遇到重复部分,则修改root1该节点相应的值。 /*** Definition for a binary tree node.* struct TreeNode {*…

超市零售数据可视化分析(Plotly 指南)

CSDN 上不能插入 HTML,可以在 GitHub Page 上查看: https://paradiseeee.github.io/2022/07/30/超市零售数据可视化分析/ 项目首次发布于 Kesci 上 – 超市零售数据分析。感兴趣的可以直接上去 Fork 之后自己做。由于上面只能用 Jupyter Notebook&#x…

多旋翼无人机试验系统设计与实现

摘 要 世界的航空业的大门被20世纪莱特兄弟制造的“飞行者一号”开启,直至今日处于飞速发展的阶段。随着时代的进步,各种微电子、微传感、通信技术的飞速发展,让无人机在时代内成为一种新型的空中力量。除了军用方面的多种用途,无…

《Linux操作系统编程》第九章 数据查找和筛选工具 : 了解流编辑器sed和报表生成器awk的简单使用

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~&#x1f33…

uni-app滚动分页 兼容(App 小程序 H5)

因为手机端本身屏幕空间不大 所以大家一般都会选择用滚动分页 首先 我在根目录下创建了一个 api目录 下面创建了一个bookApi.js 其中写了一个请求函数 getBookList 根据当前页 page 和 每页展示多少条 pageSize 获取数据 那么 我的组件代码是这样的 <template><scro…

MacBook Pro Apple M2 Max安装MySQL-8.0.33

文章目录 下载安装程序安装数据库配环境变量查看数据库状态 系统&#xff1a;macOS Ventura 芯片&#xff1a;M2 数据库&#xff1a;MySQL 8.0.33 下载安装程序 官网地址&#xff1a;https://www.mysql.com/ 点击页签“DOWLOADS“后将页面拖到底部&#xff0c;点MySQL Commu…

U盘的文件系统为FAT32才可以同时在苹果电脑和windows电脑中正常使用

文章目录 1.驱动器F中的磁盘未被格式化。想现在格式化吗&#xff1f;2.U盘插到苹果电脑上后无法写入 1.驱动器F中的磁盘未被格式化。想现在格式化吗&#xff1f; 我之前U盘的文件系统为exFAT&#xff0c;插入Windows Server 2003系统的电脑中&#xff0c;打开时弹出上面的提示框…

2023上半年软考系统分析师科目一整理-14

2023上半年软考系统分析师科目一整理-14 计算机系统性能评估中&#xff0c;( A )通常采用加法指令的运算速度来衡量计算机的速度。(D )首先计算出处理部件每个计算单元的有效计算率&#xff0c;再按不同字长加以调整&#xff0c;得出该计算单元的理论性能&#xff0c;所有组成该…

JavaFX学习:Observable Collections(观察集合)

JavaFX中的观察集合&#xff08;Observable Collections&#xff09;继承自Java的集合&#xff08;Collections&#xff09;。Java集合提供了List、Map、Set三种集合接口。JavaFX在Java集合基础上派生出可以监听集合内容变化的三种集合接口。接口如下&#xff1a; ObservableL…

【算法与数据结构】剑指 Offer 05、LeetCode替换空格

文章目录 一、题目二、双指针法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、双指针法 思路分析&#xff1a;这道题使用双指针法就能不用额外的辅助空间。首先计算字符串中的空格数量&#xff0c;然后重设…

函数模板和类模板 知识点总结 C++程序设计与算法笔记总结(七) 北京大学 郭炜

函数模板 交换两个整型变量的值的Swap函数&#xff1a; void Swap(int & x,int & y) { int tmp x; x y; y tmp; } 交换两个double型变量的值的Swap函数: void Swap(double & x,double & y) { double tmp x; x y; y tmp; }用函数模板解决&#xff1a; …

MAYA动力学曲线带动骨骼

例子 2 自由下落了 对比测试 尖端 太麻烦&#xff0c;使用风 nucleus1.windDirectionZ10*sin(time) 把球合成一个 删除一个解算器&#xff0c;就不动了

Redis 性能管理/优化 双一致性问题 缓存雪崩/击穿/穿透

---------------------- Redis 性能管理 ---------------------------------------- ----- 查看Redis内存使用 ----- info memoryredis-cli -a abc123 info memory ----- 内存碎片率 ----- used_memory_rss&#xff1a;是Redis向操作系统申请的内存。used_memory&#xff1a;是…

从有序顺序表中删除所有其值重复的元素(用不同的负数代替),使所有元素的值均不同。

题目要求&#xff1a;从有序顺序表中删除所有其值重复的元素&#xff08;用不同的负数代替&#xff09;&#xff0c;使所有元素的值均不同。 0&#xff1a;有序顺序表 1&#xff1a;删除所有其值重复的元素 2&#xff1a;用不同的负数代替 3&#xff1a;顺序表中所有元素的值均…

Maven中依赖使用范围

IDEA中help中show Log in Explorer可以查看idea日志 依赖使用范围 构建包含的流程&#xff1a;编译 &#xff0c;测试 &#xff0c;运行 &#xff0c;打包 &#xff0c;安装 &#xff0c;部署 comile test package install deploy 使用标签 1&#xff1a;compile 缺省值 伴随者…

OpenStack(T版)——块存储(Cinder)服务介绍与安装

文章目录 OpenStack(T版)——块存储(Cinder)服务介绍与安装安装和配置(controller)准备(1)创建数据库(2)加载admin user的环境变量(3)创建Identity服务凭据(4)创建Cinder 块存储服务组件的API endpoint 安装和配置Cinder块存储服务组件(1)安装软件包(2)编辑/etc/cinder/cinder.…

第七章:使用FileZilla搭建FTP服务器详解

目录 一、软件下载 二、服务器安装与配置 三、使用客户端 一、软件下载 到官方网站下载 FileZilla 的服务端和客户端程序 &#xff1a; FileZilla - The free FTP solution 二、服务器安装与配置 1 安装 安装的过程非常简单&#xff0c;直接下一步就可以了&#xff0c;需要…

【热部署】springboot-devtools

目录 pom idea配置 1 2 2021往后的idea版本 之前的idea版本 3 说明 注意 pom <!-- <!&ndash; 热部署 &ndash;>--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devt…

前端开发中的ajax请求、axios封装

目录 浏览器http请求 同步 js标签跨域、url 异步ajax、websock协议 ajax是异步的技术术语&#xff0c;最早的api是xhr&#xff08;XMLHttpRequest&#xff09; fetch es6 api axios 封装axios src/utils/request.ts src/utils/func.ts SSO&#xff08;Single Sign-On…