系统设计-Ch15-设计Google Drive
设计Google Drive
近年来,谷歌网盘、Dropbox、微软One driveh和苹果iCoud等云存储服务变得非常流行。在本章中,您被要求设计google drive。
在开始设计前,让我们花点时间了解一下Google Drive。Google Drive是一项文件存储和同步服务,可以帮助您在云中存储文档、照片和其他文件。您可以从任何电脑、智能手机和平板电脑中访问您的文件。下面两张图分别展示了Google Drive在浏览器和手机app上的外观:
在本章中我们会重点介绍以下功能:
- 添加文件:最简单的添加文件方式就是把文件拖到Google Drive里
- 同步文件:当文件添加到一个设备时,它会同步到另一个文件
- 共享文件:文件可以通过链接、二维码等方式分享给其他人
- 通知:当文件被编辑、删除或共享时,发送通知
Step1:Understand the problem and establish design scope
对于”设计Google Drive“这个系统设计话题,我们可以向面试官提出以下问题:
Q:最重要的功能是什么?
A:上传和下载文件、文件同步和通知。
Q:我们要设计的是手机app还是web网页,还是二者兼而有之?
A:二者都有
Q:支持那些文件格式?
A:任何文件类型
Q:文件需要加密么?
A:是的,存储的文件必须加密
Q:有文件大小限制么?
A: 单个文件暂时不设置大小限制,但总存储容量初始默认为10GB
Q: 这个产品的日活跃度是多少?
A: 1000W日活跃用户
Step2:Propose high-level design and get buy-in
这一章中,我们不一开始就展示high-level design设计图。我们先从简单的设计出发—在单个服务器中构建所有内容,然后再逐步扩大规模以支持数百万用户。通过做这个练习,它将帮你回忆起书中涉及的一些重要主题的内容。
让我们从下面列出的单个服务器设置开始:
- 用于上传和下载文件的网络服务器
- 一个数据库,用于跟踪元数据,如用户数据,登录信息、文件信息等
- 用于存储文件的存储系统,我们分配1TB的存储空间
- 我们花了几个小时设置一个apache web服务器、一个mysql数据库和一个名为drive的目录作为根目录来存储上传的文件。在drive目录下,有一个目录列表,称为namespace。每个namespace都包含该用户的所有上传文件。服务器上的文件名与原始文件名保持不变。每个文件或文件夹,都可以通过链接namespace和相对路径进行唯一标识。
下图展示了drive目录的示例,左侧按用户划分namespace,右侧是目录的展开图:
API
我们需要三个api:upload file, download file, get file revisions
Upload file
我们需要支持两种类型的文件上传:
- Simple upload(简单上传):当文件很小时,我们直接使用简单的上传
- resumable upload(断点续传):当文件大小很大时,上传需要花费较长时间,网络中断的可能性较高
Download file
下载需要记录下载路径
Get file revisions
记录文件的历史版本,版本有最大保留次数,超过该限制的版本不再保存。
上述所有api都需要用户身份验证并使用https通信。安全套接字保护客户端和后端服务器之间的数据传输。
Move away from single server
随着上传的文件越来越多,单个服务器满足不了存储需求。
我们想到的第一个解决方案时对数据进行分片,以便将数据存储在多个存储服务器上。下图显示了一个基于user_id的分片示例:
数据库分片是一种可行的存储方案,但是仍然有存储服务器中断的情况下丢失数据的风险。许多领先的公司都使用Amazon S3是一个对象存储服务,提供业界领先的可扩展性、数据可用性、安全性和高性能。
Amazon S3支持同区域和跨区域复制。区域是指亚马逊网络服务拥有数据中心的地理区域。如下图所示:
可以在同区域(左侧)和跨区域(右侧)复制数据。冗余文件存储在多个区域中,以防止数据丢失并确保可用性。bucket就像文件系统中的文件夹。
添加了文件存储后,你可以添加负载均衡器、web服务器、元数据数据库来继续升级,这些东西在之前的章节中已经详细介绍了,这里就不详细展开,扩展后的high-level design如下图所示:
sync confilct
对于像google drive这样的大型存储系统,同步冲突时有发生。当两个用户同时修改一个文件或文件夹时,会发生冲突。那么如何解决同步冲突呢?这是我们的策略,第一个处理的版本获胜,稍后处理的版本会收到冲突。下图显示了一个同步冲突的示例:
在上图中,用户1和用户2试图同时更新同一个文件,但用户1的文件首先由我们的系统处理,用户1的更新操作完成,但是用户2遇到同步冲突。我们如何解决用户2的冲突?我们的系统提供同一个文件的两个副本:用户2的本地副本和服务器的最新版本。用户2可以选择合并两个文件或用另一个版本替换上一个版本。
high-level design
下图展示了当前的high-level design设计图:
- User: 用户通过浏览器或移动应用城西使用该google drive
- Block servers:区块服务器将区块上传到云服务器。一个文件可以分为几个块,每个块都有唯一的哈希值,存储在元数据库中
- Cloud Storage:云存储,文件被划分为块,存储在云服务中
- Cold Storage:冷存储是一种用于存储非活跃数据的计算机系统,这意味着文件在很长一段时间内不会被访问。
- Load balancer:负载均衡器
- API server:API服务器负责处理非常多的常规请求
- Metadata database: 元数据库,存储用户,文件,块,版本等元数据。
- Metadata cache:为了快速检索,缓存了一些元数据
- Notification service:它是一个发布者、订阅者系统,允许在某些事件发生时推送通知。在Google drive中,当发生添加、编辑、删除文件时,通知服务会通知相关用户
- Offline backup queue:如果客户端脱机导致无法拉取最新修改,则该队列会存储信息,以便在客户端联机时同步更改
有些组件很复杂,我们将在Step3中详细讨论这些问题。
Step3:Design deep dive
在本节中,我们将仔细探讨以下内容:block servers,meta database,upload flow,download flow, notification service, save storage space.
Block servers
对于定期更新的大文件,每次更新时发送整个文件会消耗大量带宽。,这里提出了两种优化方案,可以最大限度地减少正在传输的网络流量:
- 增量同步:当修改文件时,使用同步算法。只同步跟新修改后的块,而不是整个文件。
- 压缩:对块应用压缩可以显著减少数据大小。不同的文件类型需要使用不同的压缩算法,例如,gzip和bzip2用于压缩文本文件。
因此,在我们的系统中,我们设计了块服务器为上传文件做繁重的工作。块服务器通过将文件拆分为块、压缩每个块来处理从客户端上传的文件,如下图所示:
下图显示了增量同步,这意味着只有修改后的块才能传输到云存储。高亮显示的块表示已更改的块,只有这两个块被上传到云存储:
High consistency requirement
默认情况下,我们的系统需要强一致性:不同客户端从云上拉取的数据必须要保证相同。
因此,该系统需要为元数据缓存和数据库层提供强一致性。默认情况下,内存缓存采用最终一致性模型,这意味着不同的副本可能具有不同的数据,为了实现强大的一致性,我们必须确保以下几点:
- 缓存副本和主机中的数据时一致的
- 在数据库写入时使缓存无效,以确保缓存和数据库保存相同的值。在关系数据库中实现强一致性很容易,因为它维护ACID(原子性、一致性、隔离性、持久性)。但是默认情况下,NoSQL不支持ACID属性。因此在我们的设计中,我们选择关系型数据库。
Meta database
下图展示了数据库架构设计。请注意,这只是一个高度简化的版本,因为它只包含最重要的表和关键字段。
Download flow
在其他地方添加或编辑文件时,将触发下载流程:
如果客户端A在另一个客户端更改文件时处于联机状态,则通知服务将通知客户端A“在某处进行了更改,需要拉取最新云端数据”。如果客户端A脱机,另一个客户端更改了文件,则数据将保存在缓存中,等到联机时再同步。
一旦客户端知道文件已更改,它首先通过API服务器请求元数据,然后下载块来构建文件。下图显示了详细流程,由于空间限制,图中仅显示了最重要的组件:
- 通知服务通知客户端,某个文件在其他地方发生了更改
- 一旦客户端知道了有新的更新可用,它就发送获取元数据更新的请求
- API服务器调用元数据D来获取变更的元数据
- 元数据返回API服务器
- 客户端获取元数据
- 一旦客户端接收到元数据,它就向块服务器发送下载块的请求
- 云存储将块返回到块服务器
- 客户端重新下载所有新的块来重构文件
Notification service
为了保持文件一致性,本地执行的任何文件冲突都要通知其他客户端,以减少冲突,通知服务就是为了这个目的而构建的。
在上一章中,通知服务允许在事件发生时将数据将传输到客户端。以下时一些选项:
- 长时间轮询, Dropbox是用长时间轮询机制来发送通知
- Web Socket。WebSocket 提供双向持久连接
虽然这里两种都可以,但是因为双向持久连接会占用服务器资源,而且这种通知服务没有像聊天服务那样高频,因此长时间轮询显然时更佳的选择。
Save storage space
为了支持文件版本历史记录并确保可靠性,同一文件的多个版本存储在多个数据中心中。频繁备份文件的多个版本会快速占用服务器的存储空间,这里提出了三种降低存储成本的技术:
- 消除重复数据块:MD5哈希值来鉴别重复数据块
- 采用智能数据备份策略:设置存储版本数量限制或只保留有价值的版本
- 将不常用的数据转移到冷库
Failure Handling
这里的内容都是无意义的重复内容,就不翻译了
Step4:Warm up
如果还有几分钟时间,你可以谈谈不同的设计选择,例如:
- 首先,我们可以从客户端直接将文件上传到云存储,而不是通过块服务器间接上传
- 客户端由于很容易被黑客入侵,因此在客户端实现存储加密并不理想,转移到服务器加密该怎么做?
祝贺你完成了所有章节的学习,Good job!
本博客所有文章均采用 CC BY-NC-SA 4.0 协议 ,禁止商用,转载请注明出处!