【系统设计】S3 对象存储

f86c9deec3d8d0cfc95574d5f248e1a0.gif

在本文中,我们设计了一个类似于 Amazon Simple Storage Service (S3) 的对象存储服务。S3 是 Amazon Web Services (AWS) 提供的一项服务, 它通过基于 RESTful API 的接口提供对象存储。根据亚马逊的报告,到 2021 年,有超过 100 万亿个对象存储在 S3 中。

在深入设计之前,有必要先回顾一下存储系统和相关的术语。

存储系统

在高层次上,存储系统分类三大类:

  • • 块存储

  • • 文件存储

  • • 对象存储

块存储

块存储最早出现在 1960 年。常见的物理存储设备,比如常说的 HDD 和 SSD 都属于块存储。块存储直接暴露出来卷或者盘,这是最灵活,最通用的存储形式。

块存储不局限于物理连接的存储,也可以通过网络、光纤和 iSCSI 行业标准协议连接到服务器。从概念上讲,网络附加块存储仍然暴露原始块,对于服务器来说,它的工作方式和使用物理连接的块存储是相同的。

文件存储

文件存储在块存储的上层,提供了更高级别的抽象,文件存储不需要处理管理块、格式化卷等,所以它处理文件和目录更简单,数据文件存储在分层目录结构。

对象存储

对象存储相对来说比较新,为了高持久性,大规模和低成本而牺牲性能,这是一个非常刻意的权衡。对象存储针对的是相对 “冷” 的数据,主要用于归档和备份。对象存储把所有的数据作为对象存储在平面结构中,没有分层的目录结构。

通常提供了 RESTful API 用来支持数据访问,和其他的存储相比,它是比较慢的,大多云服务商都提供了对象存储的产品,比如 AWS S3, Azure Blob 存储等。

对比

905a8f1b6863d12633818efc94fb0417.png

术语

要设计一个类似于 S3 的对象存储,我们需要先了解一些对象存储的核心概念。

  • • 桶 (Bucket),桶是对象的逻辑容器,存储桶名称是全局唯一的。

  • • 对象(Object),对象时我们存储在桶中的单个数据,它由对象数据和元数据组成。对象可以是我们存储的任何字节序列,元数据是一组描述对象的键值对。

  • • 版本控制 (Versioning), 数据更新时,允许多版本共存。

  • • 统一资源标识符 (URI),对象存储提供了 RESTful API 来访问资源,所以每个资源都有一个URI 唯一标识。

  • • 服务等级协议 (SLA),SLA 是服务提供商和客户之间的协议。比如 AWS S3 对象存储,提供了 99.9 的可用性,以及夸张的 99.999999999% (11个9) 的数据持久性。

设计要求

8c8b8beadc44a47f9413c4b832bfc5de.png

在这个面试的系统设计环节中,需要设计一个对象存储,并且要满足下面的几个要求。

  • • 基础功能,桶管理,对象上传和下载,版本控制。

  • • 对象数据有可能是大对象(几个 GB),也可能是小对象(几十 kb)。

  • • 一年需要存储 100 PB 的数据。

  • • 服务可用性 99.99% (4个9), 数据持久性 99.9999 % (6个 9)。

  • • 需要比较低的存储成本。

对象存储的特点

在开始设计对象存储之前,你需要了解它的下面这些特点。

对象不变性

对象存储和其他两种存储的主要区别是,存储对象是不可变的,允许进行删除或者完全更新,但是不能进行增量修改。

键值存储

我们可以使用 URI 来访问对象数据,对象的 URI 是键,对象的数据是值,如下

Request:
GET /bucket1/object1.txt HTTP/1.1Response:
HTTP/1.1 200 OK
Content-Length: 4567[4567 bytes of object data]

写一次,读多次

对象数据的访问模式是一次写入,多次读取。根据 LinkedIn 做的研究报告,95 %的请求是读取操作。

【Ambry: LinkedIn’s Scalable Geo-Distributed Object Store】

支持小型和大型对象

对象存储的设计理念和 UNIX 文件系统的设计理念非常相似。在 UNIX 中,当我们在本地文件系统中保存文件时,它不会把文件名和文件数据一起保存。那是怎么做的呢?它把文件名存储在 inode 的数据结构中,把文件数据存储在不同的磁盘位置。inode 包含一个文件块指针列表,这些指针指向文件数据的磁盘位置。当我们访问本地文件时,首先会获取 inode 中的元数据。然后我们按照文件块指针来读取磁盘的文件数据。

对象存储的工作方式也是如此,元数据和数据存储分离,如下

af2777b27fd3721ac70527968cb71f43.png

看一看我们的存储桶和对象的设计

37d2ca273ab490ac91912b82f60c86fc.png

整体设计

下图显示了对象存储的整体设计。

1730349eabed9f7409d07423628ff630.png
  • • Load balancer 负载均衡,向多个 API 服务分发 RESTful API 请求。

  • • API Service,编排身份验证服务,元数据服务和存储服务,它是无状态的,可以很好的支持水平扩展。

  • • Identity & Access Management (IAM),身份和访问管理,这是处理身份验证、授权和访问控制的服务。

  • • Data Store 数据存储,存储和检索对象数据,所有和数据有关的操作都是基于对象 ID(UUID)。

  • • Metadata Service 元数据服务,存储对象的元数据。

接下来我们一起来探索对象存储中的一些重要的工作流程。

  • • 上传对象

  • • 下载对象

  • • 版本控制

上传对象

c760c7289e1de285caf3fdb9da56fa86.png

在上面的流程中,我们首先创建了一个名为 "bucket-to-share" 的存储桶,然后把一个名为 "script.txt" 的文件上传到这个桶。

  1. 1. 客户端发送一个创建 “bucket-to-share” 桶的 HTTP PUT 请求,经过负载均衡器转发到 API 服务。

  2. 2. API 服务调用 IAM 确保用户已获得授权并且有 Write 权限。

  3. 3. API 服务调用元数据服务,创建存储桶,并返回成功给客户端。

  4. 4. 客户端发送创建 “script.txt” 对象的 HTTP PUT 请求。

  5. 5. API 服务验证用户的身份并确保用户对存储桶具有 Write 权限。

  6. 6. API 服务把 HTTP 请求发到到数据存储服务,完成存储后返回对象的 UUID。

  7. 7. 调用元数据服务并创建元数据项,格式如下

    6537a9b63132580ec3cac3879ec99175.png

上传数据的 Http 请求示例如下

69d60ef85e7ae8db70231639c323edc5.png

下载对象

存储对象可以通过 HTTP GET 请求进行下载,示例如下

39ed4c3b476b841ea90ead52fe2e683f.png

下载流程图

1a153022e97e42bb48817eb6b04433b8.png
  1. 1. 客户端发送 GET 请求,GET /bucket-to-share/script.txt

  2. 2. API 服务查询 IAM 验证用户是否有对应桶的读取权限。

  3. 3. 验证后,API 服务会从元数据服务中获取对象的 UUID。

  4. 4. 通过 对象的 UUID 从数据存储中获取相应的对象。

  5. 5. API 服务返回对象给客户端。

深入设计

接下来,我们会讨论下面几个比较重要的部分。

  • • 数据一致性

  • • 元数据

  • • 版本控制

  • • 优化大文件的上传

  • • 垃圾收集 GC

数据一致性

对象数据只存放在单个节点肯定是不行的,为了保证高可用,需要把数据复制到多个节点。这种情况下,我们需要考虑到一致性和性能问题。

d8563071bd5bbf03790018acf9cb5400.png

保证强一致性就要牺牲性能,如果性能要求比较高时,可以选择弱一致性。鱼和熊掌不可兼得。

数据存储方式

对于数据存储,一个简单的方式是把每个对象都存储在一个独立的文件中,这样当然是可以的。但是,当有大量的小型文件时,会有下面两个问题。

第一个问题是,会浪费很多数据块。文件系统把文件存储在磁盘块中,磁盘块的大小在卷初始化的时候就固定了,一般是 4 kb。所以,对于小于 4kb 的文件,它也会占满整个磁盘块。如果文件系统中保存了大量的小文件,那就会就会有很多浪费。

第二个问题是,系统的 inode 容量是有限的。文件系统把文件元数据存储在 inode 特殊类型的磁盘块中。对于大多数文件系统,inode 的数量在磁盘初始化时是固定的。所以有大量的文件时,要考虑到 inode 容量满的问题。

为了解决这个问题,我们可以把很多小文件合并到一个更大的文件中。从概念上讲,类似于预写日志(WAL)。当我们保存一个对象时,它被附加到一个现有的文件中。文件大小达到一定值(比如说 1 GB)后,创建一个新的文件来存储对象,下图解释了它的工作流程。

0df051c75649659ee8814cd6dabe0647.png

数据持久性

对存储系统来说,数据持久性非常重要,如何设计出一个 6 个 9 (99.9999%) 持久性 的存储系统?

硬件故障和故障域

无论使用哪种存储,硬件故障都是不可避免的。所以为了数据持久性,需要把数据复制到多个硬盘中。

假设硬盘的年故障率是 0.81 %, 当然不同的型号和品牌这些是不一样的,那个我们需要三个数据副本,1-(0.0081)^3=~0.999999, 才可以满足要求。

另外,我们还需要考虑到不同故障域的影响。这样可以在极端情况下,带来更好的可靠性,比如大规模停电,自然灾害等。

3188356e612f6772df27f4acb8385a13.png

Erasure Coding 纠删码

上面提到,我们用三个完整的数据副本可以提供大概 6 个 9 的数据持久性,但是,这样的成本太高了。

还能不能优化呢?我们可以使用纠删码技术,它的原理其实很简单,假设现在有 a 和 b 两条数据,进行异或 (XOR)运算后得到 c,a ^ b = c , 而 b = c ^ a,a = c ^ b,所以这三条数据丢失任意一条数据,都可以通过剩余两条数据计算出丢失数据。

下面是一个 4 +2 纠删码的例子。

3e21667fe2909f754ca4d236006fbad7.png
  1. 1. 数据被分成四个大小均匀的数据块 d1、d2、d3 和 d4。

  2. 2. 使用 Reed-Solomon 数学公式计算校验块,比如 p1 = d1 + 2*d2 - d3 + 4*d4 
    p2 = -d1 + 5*d2 + d3 - 3*d4 

  3. 3. 节点崩溃,导致数据 d3 和 d4 丢失。

  4. 4. 通过数据公式和现有数据,计算出丢失的数据并恢复。d3 = 3*p1 + 4*p2 + d1 - 26*d2
    d4 = p1 + p2 - 7*d2

和多副本复制相比,纠删码占用的存储空间更少。但是,在进行丢失数据恢复时,它需要先根据现有数据计算出丢失数据,这也消耗了 CPU 资源。

数据完整性校验

纠删码技术在保证数据持久性的同时,也降低的存储成本。接下来,我们可以继续解决下一个难题:数据损坏。

我们可以给数据通过 Checksum 算法计算出校验和。常见的 checksum 算法有 MD5, SHA1 等。

55e814a94c732b50f990f47726666f98.png

当需要验证数据时,只需要对比校验和即可,如果不一致,说明文件数据发生了改变。

我们同样可以把校验和添加到存储系统中,对于读写文件,每个对象都计算校验和,而对于只读文件,只需要在文件的末尾添加上整个文件的校验和即可。

7f3fad0a1b02a7c49d85b6bbe515edb5.png

版本控制

版本控制可以让一个对象的多个版本同时保存在存储桶中。这样的好处是,我们可以恢复意外删除或者覆盖的对象。

为了支持版本控制,元数据存储的列表中需要有一个 object_version 的列。上传对象文件时,不是直接覆盖现有的记录,而是插入一个新记录。

8eedf02270424d01a40a5867bdc16860.png

当进行对象删除的时候,不需要删除这条记录,而是添加一个删除标记即可,然后等垃圾收集器自动处理它。

2b1431658de764cc442b86c173e23732.png

优化大文件上传

对于比较大的对象文件(可能有几个 GB),上传可能需要较长的时间。如果在上传过程中网络连接失败,就要重新进行上传了。

为了解决这个问题,我们可以使用分段上传,上传失败时可以快速恢复。

c77141afeb3aefb25f4592b5627f75a9.png
  1. 1. 客户端调用对象存储服务发起分段上传请求。

  2. 2. 数据存储服务返回一个唯一的 uploadID。

  3. 3. 客户端把大文件拆分为小对象并开始上传,假设文件大小是 1.6 GB, 每个部分的大小是 200 MB, 客户端上传第一部分和 uploadID 。

  4. 4. 上传第一部分后,数据存储服务会返回一个 ETag,本质上它是第一部分的 md5 校验和,客户端通过它来判断数据是否发生了更改,如果是则重新上传。

  5. 5. 当每个部分都上传成功后,客户端发送一个分段上传成功的请求。

  6. 6. 数据存储服务组装小对象为大文件,并返回一个成功消息。

垃圾收集 GC

垃圾收集是自动回收不再使用的存储空间的过程,数据可能变成垃圾的几种方式:

  • • 延迟删除的对象,对象在删除时标记成已删除,但实际上还没有删除。

  • • 孤儿数据,比如上传一半的数据。

  • • 损坏的数据。

对于需要删除的对象,我们使用压缩机制定期清理,下图显示了它的工作流程。

13570168df1ad0786cac8155f892a267.png
  1. 1. 垃圾收集器把对象 “/data/b”复制到一个名为“/data/d”的新文件中。这里会跳过对象 2 和 5,因为它们的删除标志都是 true。

  2. 2. 复制完所有的对象后,垃圾收集器会更新 object_mapping 表,指向新的文件地址,然后删除掉旧的文件。

总结

在本文中,介绍了类似于 S3 的对象存储,比较了块存储、文件存储和对象存储之间的区别,设计了对象上传,对象下载,版本控制功能,并讨论了两种提高可靠性和持久性的方法:复制和纠删码,最后介绍了对象存储的垃圾收集的工作流程。

希望这篇设计对象存储的文章对大家有用!

Reference

[0] System Design Interview Volume 2:
https://www.amazon.com/System-Design-Interview-Insiders-Guide/dp/1736049119

[1] Fibre channel: https://en.wikipedia.org/wiki/Fibre_Channel

[2] iSCSI: https://en.wikipedia.org/wiki/ISCSI

[3] Server Message Block: https://en.wikipedia.org/wiki/Server_Message_Block

[4] Network File System: https://en.wikipedia.org/wiki/Network_File_System

[5] Amazon S3 Strong Consistency: https://aws.amazon.com/s3/consistency/

[6] Serial Attached SCSI: https://en.wikipedia.org/wiki/Serial_Attached_SCSI

[7] AWS CLI ls command: https://docs.aws.amazon.com/cli/latest/reference/s3/ls.html

[8] Amazon S3 Service Level Agreement: https://aws.amazon.com/s3/sla/

[9] Ambry: LinkedIn’s Scalable Geo-Distributed Object Store: https://assured-cloud-computing.illinois.edu/files/2014/03/Ambry-LinkedIns-Scalable-GeoDistributed-Object-Store.pdf

[10] inode: https://en.wikipedia.org/wiki/Inode

[11] Ceph’s Rados Gateway: https://docs.ceph.com/en/pacific/radosgw/index.html

[12] grpc: https://grpc.io/

[13] Paxos: https://en.wikipedia.org/wiki/Paxos_(computer_science)

[14] Raft: https://raft.github.io/

[15] Consistent hashing: https://www.toptal.com/big-data/consistent-hashing

[16] RocksDB: https://github.com/facebook/rocksdb

[17] SSTable: https://www.igvita.com/2012/02/06/sstable-and-log-structured-storage-leveldb/

[18] B+ tree: https://en.wikipedia.org/wiki/B%2B_tree

[19] SQLite: https://www.sqlite.org/index.html

[20] Data Durability Calculation: https://www.backblaze.com/blog/cloud-storage-durability/

[21] Rack: https://en.wikipedia.org/wiki/19-inch_rack

[22] Erasure Coding: https://en.wikipedia.org/wiki/Erasure_code

[23] Reed–Solomon error correction: https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction

[24] Erasure Coding Demystified: https://www.youtube.com/watch?v=Q5kVuM7zEUI

[25] Checksum:https://en.wikipedia.org/wiki/Checksum

[26] Md5: https://en.wikipedia.org/wiki/MD5

[27] Sha1: https://en.wikipedia.org/wiki/SHA-1

[28] Hmac: https://en.wikipedia.org/wiki/HMAC

[29] TIMEUUID: https://docs.datastax.com/en/cql-oss/3.3/cql/cql_reference/timeuuid_functions_r.html

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

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

相关文章

转: telnet命令学习

1.每天一个linux命令(58):telnet命令 转自: http://www.cnblogs.com/peida/archive/2013/03/13/2956992.html telnet命令通常用来远程登录。telnet程序是基于TELNET协议的远程登录客户端程序。Telnet协议是TCP/IP协议族中的一员&a…

禅道、码云、coding、redmine、jira、teambition几大敏捷开发项目管理系统试用对比体验

作为一个软件公司的管理人员,在项目和人员多起来后,就需要通过系统来对项目和人员进行管理。 我们是典型的软件外包公司,专为客户定制软件,所以我们的业务都是项目型的。因此,在管理模式上,我们就要用所谓…

Dubbo中的SPI机制

Dubbo中的SPI机制 概述 Service Provider Interface 即 SPI,是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件。可以让不同的厂商针对统一接口编写不同的实现 SPI实际上是“接口策略模式配置文件”实现的动态加载机制。在系统设计中&…

JWT:拥有我,即拥有权力

Hi,这里是桑小榆。上篇文章中,我们一起探讨了 OAuth 协议的原理以及授权认证流程,本次我们一起探讨 jwt 令牌作为授权协议的传输介质。OAuth协议规范了几个参与角色的授权标准,安全可控的授予第三方应用,第三方应用获取…

双十一到来之前,阿里AI设计师“鲁班”1天能做4000万张海报

相比较去年,“鲁班”的设计技艺有所提升。 人工智能很大程度上便利了我们的生活,现在他们甚至还能取代了一些设计师的工作,在双十一正式到来之前,淘宝的宣传已经铺天盖地,然而很多人都没想到,我们打开淘宝…

Appium移动自动化测试之获取appPackage和appActivity

方法一:直接打开Appium,点击左上角机器人图标 选择apk所在位置,如图所示,这里以ContactManager.apk为例 方法二:利用dex2jar和jd-gui这两个工具反编译apk文件 这里仍以ContactManager.apk为例 (1)重命名ContactManager.apk为Conta…

CAD转WPF: 关于CAD图纸文件转换为WPF矢量代码文件(xaml文件)的技巧

前言:下面的文章,我将会以几个很简单的步骤,来演示一下通过CAD图纸转换为XAML代码文件的方法,供大佬们参考。一、为了演示一个简单的操作,我此处先打开一个空白的CAD,等下用来进行绘制点内容使用。二、自定…

python之新式类与经典类

经典类与新式类经典类:P 或 P()--深度查找,向上查父节点新式类 :P(object)---广度查找,继承object,新式类的方法较多转载于:https://www.cnblogs.com/zyy98877/p/8574983.html

Flowportal-BPM——环境配置

环境配置: 一、控制面板→程序和功能→打开或不关闭Window功能→选择选项 二、控制面板→管理工具→Internet信息服务(IIS)管理器→左侧第一个→ISAPI和CGI限制→全部选为【允许】 三、控制面板→管理工具→Internet信息服务(IIS&…

一篇文章带你搞懂什么是DevOps?

DevOps DevOps 它的英文发音是 /de’vɒps/,类似于“迪沃普斯”,一词本身是对于 development 以及 operation 两个词的混合,其目的在于缩短系统开发的生命周期,在这过程中发布特性、修复bug以及更新均被紧密的结合。 简化的含义为…

微服务架构下分布式事务解决方案 —— 阿里GTS

1 微服务的发展 微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇,很多互联网行业巨头、开源社区等都开始了微服务的讨论和实践。Hailo有160个不同服务构成&…

重要消息丨.NET Core 3.1 将于今年12月13日结束支持

点击上方蓝字关注我们(本文阅读时间:5分钟).NET Core 3.1 将于 2022 年 12 月 13 日结束支持。此后,Microsoft 将不再为 .NET Core 3.1 提供服务更新或技术支持。我们建议尽快迁移到 .NET 6。如果您在支持日期结束后仍在使用 .NET Core 3.1&a…

产品设计的三大原则

1.它有用吗? 如果我们必须从这三个特性中选择一个作为最重要的,那就是有用性。 首要的是,一个产品必须有用。如果它无用,其它任何东西都是不相关的,因为没有人会需要它。很明显,有用性和可享用性看上去一样重要&#…

常用的17个运维监控系统

1. Zabbix Zabbix 作为企业级的网络监控工具,通过从服务器,虚拟机和网络设备收集的数据提供实时监控,自动发现,映射和可扩展等功能。 Zabbix的企业级监控软件为用户提供内置的Java应用服务器监控,硬件监控&#xff0c…

关于html-三角的制作

因为最近看到别人写的不错的样式,所以就想自己实现,但是呢用到了一个三角形,所以稍微研究一下。效果是这样的:注意是下边那个浅色三角,感觉书签的效果有木有。看着很有层次感。接下来就是实现了,利用border…

ABP中的数据过滤器

本文首先介绍了ABP内置的软删除过滤器(ISoftDelete)和多租户过滤器(IMultiTenant),然后介绍了如何实现一个自定义过滤器,最后介绍了在软件开发过程中遇到的实际问题,同时给出了解决问题的一个未必最优的思路。一.预定义过滤器ABP中的数据过滤…

ActiveMQ与spring整合

2019独角兽企业重金招聘Python工程师标准>>> 1 生产者 第一步&#xff1a;引用相关的jar包。 <dependency> <groupId>org.springframework</groupId><artifactId>spring-jms</artifactId> </dependency> <dependency><…

最新远程部署运维工具汇总

一&#xff0e;Puppet 转载https://baike.baidu.com/item/puppet/5109503?fraladdin puppet是一种Linux、Unix、windows平台的集中配置管理系统&#xff0c;使用自有的puppet描述语言&#xff0c;可管理配置文件、用户、cron任务、软件包、系统服务等。puppet把这些系统实体…

Kali Linux 2016.2初体验使用总结

Kali Linux 2016.2初体验使用总结Kali Linux官方于8月30日发布Kali Linux 2016的第二个版本Kali Linux 2016.2。该版本距离Kali Linux 2016.1版本发布&#xff0c;已经有7个月。在这期间&#xff0c;在Kali Linux 2016.2版本发布的这段时间&#xff0c;Kali Linux官方增补了94个…

Kafka入门教程:学习总结目录索引

【Kafka】| 总结/Edison ZhouEdison总结了Kafka的学习征途系列&#xff0c;特意整理了一份目录索引&#xff0c;希望对你有帮助。0Kafka学习路径在学习Kafka的途中&#xff0c;我总结了一个系列的Kafka学习征途系列教程&#xff0c;它只选取了我认为最实用的部分整理出来&#…