前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【系统设计】S3 对象存储

【系统设计】S3 对象存储

作者头像
全球技术精选
发布于 2022-09-05 08:09:32
发布于 2022-09-05 08:09:32
7.1K00
代码可运行
举报
文章被收录于专栏:全球技术精选全球技术精选
运行总次数:0
代码可运行

在本文中,我们设计了一个类似于 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 存储等。

对比

术语

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

  • • 桶 (Bucket),桶是对象的逻辑容器,存储桶名称是全局唯一的。
  • • 对象(Object),对象时我们存储在桶中的单个数据,它由对象数据和元数据组成。对象可以是我们存储的任何字节序列,元数据是一组描述对象的键值对。
  • • 版本控制 (Versioning), 数据更新时,允许多版本共存。
  • • 统一资源标识符 (URI),对象存储提供了 RESTful API 来访问资源,所以每个资源都有一个URI 唯一标识。
  • • 服务等级协议 (SLA),SLA 是服务提供商和客户之间的协议。比如 AWS S3 对象存储,提供了 99.9 的可用性,以及夸张的 99.999999999% (11个9) 的数据持久性

设计要求

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

  • • 基础功能,桶管理,对象上传和下载,版本控制。
  • • 对象数据有可能是大对象(几个 GB),也可能是小对象(几十 kb)。
  • • 一年需要存储 100 PB 的数据。
  • • 服务可用性 99.99% (4个9), 数据持久性 99.9999 % (6个 9)。
  • • 需要比较低的存储成本。

对象存储的特点

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

对象不变性

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

键值存储

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Request:
GET /bucket1/object1.txt HTTP/1.1

Response:
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 中的元数据。然后我们按照文件块指针来读取磁盘的文件数据。

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

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

整体设计

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

  • • Load balancer 负载均衡,向多个 API 服务分发 RESTful API 请求。
  • • API Service,编排身份验证服务,元数据服务和存储服务,它是无状态的,可以很好的支持水平扩展。
  • • Identity & Access Management (IAM),身份和访问管理,这是处理身份验证、授权和访问控制的服务。
  • • Data Store 数据存储,存储和检索对象数据,所有和数据有关的操作都是基于对象 ID(UUID)。
  • • Metadata Service 元数据服务,存储对象的元数据。

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

  • • 上传对象
  • • 下载对象
  • • 版本控制

上传对象

在上面的流程中,我们首先创建了一个名为 "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. 调用元数据服务并创建元数据项,格式如下

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

下载对象

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

下载流程图

  1. 1. 客户端发送 GET 请求,GET /bucket-to-share/script.txt
  2. 2. API 服务查询 IAM 验证用户是否有对应桶的读取权限。
  3. 3. 验证后,API 服务会从元数据服务中获取对象的 UUID。
  4. 4. 通过 对象的 UUID 从数据存储中获取相应的对象。
  5. 5. API 服务返回对象给客户端。

深入设计

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

  • 数据一致性
  • • 元数据
  • • 版本控制
  • • 优化大文件的上传
  • • 垃圾收集 GC

数据一致性

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

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

数据存储方式

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

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

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

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

数据持久性

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

硬件故障和故障域

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

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

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

Erasure Coding 纠删码

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

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

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

  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 等。

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

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

版本控制

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

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

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

优化大文件上传

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

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

  1. 1. 客户端调用对象存储服务发起分段上传请求。
  2. 2. 数据存储服务返回一个唯一的 uploadID。
  3. 3. 客户端把大文件拆分为小对象并开始上传,假设文件大小是 1.6 GB, 每个部分的大小是 200 MB, 客户端上传第一部分和 uploadID 。
  4. 4. 上传第一部分后,数据存储服务会返回一个 ETag,本质上它是第一部分的 md5 校验和,客户端通过它来判断数据是否发生了更改,如果是则重新上传。
  5. 5. 当每个部分都上传成功后,客户端发送一个分段上传成功的请求。
  6. 6. 数据存储服务组装小对象为大文件,并返回一个成功消息。

垃圾收集 GC

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

  • • 延迟删除的对象,对象在删除时标记成已删除,但实际上还没有删除。
  • • 孤儿数据,比如上传一半的数据。
  • • 损坏的数据。

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

  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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-08-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 半栈程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
对象存储入门
10.5.3 对象接口 对象存储系统(Object-BasedStorage System)是综合了NAS和SAN的优点,同时具有SAN的高速直接访问和NAS的数据共享等优势,提供了高可用性、跨平台性及安全性的数据共享的存储体系结构。 Object是对象存储的基本单元。每个Object都是数据和数据属性集的综合体。数据属性可以根据应用的需求进行设置,包括数据分布、服务质量等。在传统的存储中,块设备要记录每个存储数据块在设备上的位置。Object维护自己的属性,从而简化了存储系统的管理任务,增加了灵活性。O
大数据和云计算技术
2018/03/08
7.5K0
对象存储入门
对象存储来势汹汹,究竟谁是“幕后推手”?
对象存储来势汹汹,究竟谁是“幕后推手”?
数据猿
2018/04/19
1.2K0
对象存储来势汹汹,究竟谁是“幕后推手”?
Github 29K Star的开源对象存储方案——Minio入门宝典
对象存储不是什么新技术了,但是从来都没有被替代掉。为什么?在这个大数据发展迅速地时代,数据已经不单单是简单的文本数据了,每天有大量的图片,视频数据产生,在短视频火爆的今天,这个数量还在增加。有数据表明,当今世界产生的数据,有80%是非关系型的。那么,对于图片,视频等数据的分析可以说是大数据与人工智能的未来发展方向之一。
用户6070864
2021/10/26
11.6K1
Github 29K Star的开源对象存储方案——Minio入门宝典
MinIO对象存储
MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
别团等shy哥发育
2023/03/30
7.5K0
MinIO对象存储
【系统设计】分布式键值数据库
键值存储 ( key-value store ),也称为 K/V 存储或键值数据库,这是一种非关系型数据库。每个值都有一个唯一的 key 关联,也就是我们常说的 键值对。
全球技术精选
2022/09/05
1.6K0
【系统设计】分布式键值数据库
听说你想把对象存储当 HDFS 用,我们这里有个方案...
传统的大数据集群往往采用本地中心化的计算和存储集群。比如在谷歌早期的【三驾马车】中,使用 GFS 进行海量网页数据存储,用 BigTable 作为数据库并为上层提供各种数据发现的能力,同时用 MapReduce 进行大规模数据处理。
云存储
2021/11/24
8540
听说你想把对象存储当 HDFS 用,我们这里有个方案...
系统设计面试的行家指南(下)
近年来,Google Drive、Dropbox、微软 OneDrive、苹果 iCloud 等云存储服务变得非常流行。在这一章中,你被要求设计 Google Drive。
ApacheCN_飞龙
2024/01/28
4620
云原生 | 从零开始,Minio 高性能分布式对象存储快速入手指南
描述: 对象存储(Object Storage)是一种存储数据的计算机体系结构,它以对象的形式存储和管理数据。与传统的文件系统和块存储不同,对象存储将数据作为对象存储在分布式的存储集群中,每个对象都有一个唯一的标识符(通常是一个URL),并且可以通过这个标识符来访问和检索数据。
全栈工程师修炼指南
2023/10/31
9.7K1
云原生 | 从零开始,Minio 高性能分布式对象存储快速入手指南
对象存储COS成本优化方案
随着上云企业越来越多,企业对用云成本问题也越发重视。业务的发展会产生海量存储需求,在云端存储数据时,如何进行成本优化,减轻业务负担呢?
云存储
2020/11/20
1.5K0
对象存储COS成本优化方案
互联网十万个为什么之什么是对象存储?
在如今这个数据爆炸的时代,高效、灵活的数据存储解决方案显得尤为重要。对象存储(Object Storage)是一种存储架构,它以对象为单位来处理、存储和检索数据。与传统的文件存储和块存储不同,对象存储将数据作为对象进行管理,每个对象都包含了数据本身、元数据以及一个全局唯一的标识符。这种独特的存储方式使得对象存储在处理大量、非结构化的数据时具有明显的优势。
linus_lin
2024/09/06
1420
互联网十万个为什么之什么是对象存储?
AWS S3 对象存储攻防
文章来源:火线Zone社区,链接:https://zone.huoxian.cn/d/907-aws-s3
火线安全
2022/03/01
3.7K0
AWS S3 对象存储攻防
自己搭建个对象存储服务难不难?
今天小编就在欢快的编码,来了一个刚毕业的小嫩青,虚心求教到 对象存储服务到底有啥用? 说起这个对象存储服务,那家伙,那场面,那可是锣鼓喧天、鞭炮齐鸣 打住,打住,其实小编对于对象存储服务的理解是,为了提供数据、文件、图片、视频这一系列对象类型的的有效储存,通俗的讲,就有点类似平时用的网盘,只不过对于公司来说,往往都是一个专业的拥有巨大空间的存储产品。 尤其是过了千禧年之后,网络的普及让数据呈现爆炸式的增长。 同时,在互联网行业中,非结构化数据的占比开始逐渐增加,所谓的非机构化数据,就是指图像、音频、视频这
程序猿DD
2023/04/04
2K0
自己搭建个对象存储服务难不难?
非结构化数据怎么存?——开源对象存储方案介绍
过去的相当长的一段时间里,商用对象存储占据了市场上的大量的份额。国外的Amazon S3,国内的阿里云OSS都成为了大多数公司的选择。但是构建一个企业级的数据湖(包括结构化和非结构化数据)已经成为了越来越多公司的目标。那么Hadoop还能满足我们的要求吗?还是我们需要更多的选择?
大数据流动
2021/09/22
2.5K0
非结构化数据怎么存?——开源对象存储方案介绍
EMR入门学习之腾讯云对象存储COS(八)
COS(Cloud Object Storage),一种海量的分布式存储服务,用户可以随时通过互联网对大量数据进行批量存储,兼具性能与共享能力,适用于大数据场景,访问接口多样化,控制台、API、SDK。
披荆斩棘
2019/11/18
3.4K0
打造企业级自动化运维平台系列(十三):分布式的对象存储系统 MinIO 详解
MinIO 是一款高性能、分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。
民工哥
2024/01/18
8K0
打造企业级自动化运维平台系列(十三):分布式的对象存储系统 MinIO 详解
腾讯云对象存储COS安全方案介绍
​相信所有企业和个人开发者在选用云存储产品时都把数据安全作为重要考量标准。 本文介绍了用户如何使用腾讯云对象存储COS的事前防护、事中监控、事后追溯三个手段来保证自己的数据安全。
云存储
2020/03/03
7.4K0
探索未来:对象存储的演进与应用
在当今数字化时代,数据量不断增长,对于存储系统提出了更高的要求。传统的存储方式已经难以满足大规模数据的存储和管理需求,因此,对象存储(Object Storage)应运而生。对象存储是一种面向海量数据的存储架构,以其高扩展性、弹性存储、高性能和简单管理等特点,成为了云计算、大数据分析和企业数据管理中的重要组成部分。
繁依Fanyi
2024/03/18
3700
对象存储,为什么那么火?
上期文章,小枣君给大家详细介绍了数据存储技术的基本知识,其中重点对DAS、SAN和NAS技术进行了对比分析。
鲜枣课堂
2020/08/04
3.3K2
对象存储,为什么那么火?
对象存储基础概念
对象存储诞生之初 谈到为什么要有对象存储,必须聊聊对象存储诞生之前的两大存储模型:块存储和文件存储。 块存储主要是将存储介质的空间整个映射给主机使用的,主机如果需要对这些空间进行读写IO操作,需要先进行分区和格式化处理,形成可以被操作系统识别的逻辑命名空间,之后主机才能通过操作系统对这些存储介质进行读写操作。常见的块存储有磁盘,SSD,NAS、SAN等,这些物理设备都或多或少存在物理上的极限,比如存储空间、性能等都存在物理极限。 文件存储立足于物理存储介质之上,是操作系统对数据管理操作的抽象,这些抽象最终汇
用户1260683
2018/03/26
5.8K0
对象存储基础概念
聊聊越来越火的对象存储
随着云计算的发展,云存储作为一种更基础的云上资源池设施也越来越受到重视和欢迎。从云存储的类型来讲,目前流行的有块存储、文件存储和对象存储三种。今天的主角是对象存储,不过我们在介绍对象存储之前,先来了解下另外两种存储,做个对比,这样才能更好的了解对象存储。
飞雪无情
2020/08/10
9300
推荐阅读
相关推荐
对象存储入门
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档