系统设计面试的行家指南(下)(1)https://developer.aliyun.com/article/1481949
同步冲突
对于 Google Drive 这样的大型存储系统,同步冲突时有发生。当两个用户同时修改同一个文件或文件夹时,就会发生冲突。我们如何解决冲突?这是我们的策略:第一个被处理的版本获胜,后面被处理的版本受到冲突。图 15-8 显示了一个同步冲突的例子。
在图 15-8 中,用户 1 和用户 2 试图同时更新同一个文件,但是用户 1 的文件首先被我们的系统处理。用户 1 的更新操作成功,但是用户 2 遇到了同步冲突。我们如何解决用户 2 的冲突?我们的系统提供了同一个文件的两个副本:用户 2 的本地副本和来自服务器的最新版本(图 15-9)。用户 2 可以选择合并两个文件,或者用一个版本覆盖另一个版本。
当多个用户同时编辑同一个文档时,保持文档同步是一项挑战。感兴趣的读者可以参考参考资料[4] [5]。
高层设计
图 15-10 说明了建议的高层设计。让我们检查一下这个系统的每个组成部分。
用户 : 用户通过浏览器或移动应用程序使用应用程序。
块服务器: 块服务器上传块到云存储。块存储,也称为块级存储,是一种在基于云的环境中存储数据文件的技术。一个文件可以分成几个块,每个块都有一个唯一的哈希值,存储在我们的元数据数据库中。每个数据块都被视为一个独立的对象,存储在我们的存储系统(S3)中。为了重建文件,块以特定的顺序连接。至于块大小,我们使用 Dropbox 作为参考:它将块的最大大小设置为 4MB [6]。
云存储: 一个文件被分割成更小的块,存储在云存储中。
冷存储: 冷存储是一种计算机系统,用于存储非活动数据,即长时间不访问的文件。
负载均衡器 :负载均衡器在 API 服务器之间平均分配请求。
API 服务器: 这些负责除了上传流程以外的几乎所有事情。API 服务器用于用户认证、管理用户配置文件、更新文件元数据等。
元数据数据库: 存储用户、文件、块、版本等元数据。请注意,文件存储在云中,元数据数据库只包含元数据。
元数据缓存 :一些元数据被缓存以便快速检索。
通知服务 :这是一个发布者/订阅者系统,当某些事件发生时,它允许数据从通知服务转移到客户端。在我们的具体案例中,当文件在其他地方被添加/编辑/删除时,通知服务会通知相关的客户端,以便他们可以获取最新的更改。
离线备份队列 :如果客户端离线,无法获取最新的文件更改,离线备份队列会存储信息,以便在客户端在线时同步更改。
我们已经在高层讨论了 Google Drive 的设计。有些部件很复杂,值得仔细检查;我们将在深潜中详细讨论这些。
步骤 3 -设计深度潜水
在本节中,我们将详细了解以下内容:块服务器、元数据库、上传流程、下载流程、通知服务、节省存储空间和故障处理。
块服务器
对于定期更新的大文件,每次更新时发送整个文件会消耗大量带宽。提出了两种优化来最小化正在传输的网络流量:
增量同步。当文件被修改时,使用同步算法[7] [8],仅同步修改的块,而不是整个文件。
压缩。对块应用压缩可以显著减小数据大小。因此,使用取决于文件类型的压缩算法来压缩块。例如,gzip 和 bzip2 用于压缩文本文件。压缩图像和视频需要不同的压缩算法。
在我们的系统中,块服务器承担上传文件的繁重工作。块服务器通过将文件分割成块、压缩每个块并加密来处理从客户端传来的文件。不是将整个文件上传到存储系统,而是只传输修改过的数据块。
图 15-11 显示了添加新文件时块服务器的工作方式。
一个文件被分割成更小的块。
使用压缩算法压缩每个块。
为了保证安全,每个块在发送到云存储之前都会进行加密。
块被上传到云存储。
图 15-12 说明了增量同步,这意味着只有修改过的数据块被传输到云存储。突出显示的块“块 2”和“块 5”代表已更改的块。使用增量同步,只有这两个块被上传到云存储。
块服务器允许我们通过提供增量同步和压缩来节省网络流量。
高一致性要求
默认情况下,我们的系统需要强一致性。不同的客户端同时以不同的方式显示一个文件是不可接受的。系统需要为元数据缓存和数据库层提供强一致性。
默认情况下,内存缓存采用最终一致性模型,这意味着不同的副本可能有不同的数据。为了实现强一致性,我们必须确保以下几点:
缓存中的数据副本和主副本是一致的。
在数据库写入时使高速缓存无效,以确保高速缓存和数据库保存相同的值。
在关系数据库中实现强一致性很容易,因为它保持了 ACID(原子性、一致性、隔离性、持久性)属性[9]。但是,默认情况下,NoSQL 数据库不支持 ACID 属性。ACID 属性必须以编程方式合并到同步逻辑中。在我们的设计中,我们选择关系数据库,因为 ACID 本身就受支持。
元数据数据库
图 15-13 显示了数据库模式设计。请注意,这是一个高度简化的版本,因为它只包括最重要的表和有趣的字段。
用户 :用户表包含用户的基本信息,如用户名、邮箱、个人资料照片等。
设备 :设备表存储设备信息。 Push_id
用于发送和接收移动推送通知。请注意,一个用户可以拥有多台设备。
命名空间 :命名空间是用户的根目录。
文件: 文件表存储了与最新文件相关的一切。
File_version :存储文件的版本历史。现有行是只读的,以保持文件修订历史的完整性。
Block :存储与一个文件块相关的一切。任何版本的文件都可以通过以正确的顺序连接所有的块来重建。
上传流量
让我们讨论一下当客户端上传文件时会发生什么。为了更好地理解流程,我们绘制了如图 15-14 所示的序列图。
在图 15-14 中,并行发送两个请求:添加文件元数据和上传文件到云存储。两个请求都来自客户端 1。
添加文件元数据。
1。客户端 1 发送添加新文件的元数据的请求。
2。将新的文件元数据存储在元数据数据库中,并将文件上传状态更改为“待定”
3。通知通知服务正在添加新文件。
4。通知服务通知相关客户端(客户端 2)正在上传文件。
上传文件到云存储。
2.1 客户端 1 将文件内容上传到块服务器。
2.2 块服务器将文件分块,压缩,加密,上传到云存储。
2.3 文件上传后,云存储触发上传完成回调。请求被发送到 API 服务器。
2.4 元数据数据库中的文件状态更改为“已上传”。
2.5 通知通知服务,文件状态已更改为“已上传”
2.6 通知服务通知相关客户端(客户端 2)文件上传完毕。
编辑一个文件的时候,流程差不多,就不赘述了。
下载流量
在别处添加或编辑文件时触发下载流。客户端如何知道文件是否被另一个客户端添加或编辑?客户端可以通过两种方式获知:
如果客户端 A 在线,而另一个客户端更改了文件,通知服务将通知客户端 A 某处发生了更改,因此它需要获取最新数据。
如果客户端 A 离线,而文件被另一个客户端更改,数据将被保存到缓存中。当脱机客户端再次联机时,它会提取最新的更改。
一旦客户端知道文件被更改,它首先通过 API 服务器请求元数据,然后下载块来构建文件。图 15-15 显示了详细的流程。请注意,由于篇幅限制,图中仅显示了最重要的组件。
1。通知服务通知客户端 2 文件在其他地方被改变。
2。一旦客户机 2 知道有新的更新可用,它就发送一个获取元数据的请求。
3。API 服务器调用元数据数据库来获取变更的元数据。
4。元数据被返回给 API 服务器。
5。客户端 2 获取元数据。
6。客户端收到元数据后,会向数据块服务器发送下载数据块的请求。
7。块服务器首先从云存储中下载块。
8。云存储将块返回给块服务器。
9。客户端 2 下载所有新块来重建文件。