在这个阶段,我们假设系统需要处理 50 万用户和 3000 万首歌曲。我们将有播放歌曲的用户和上传歌曲的艺术家。
我们首先估计我们需要的存储空间。我们需要将歌曲存储在某种存储中,以及存储歌曲元数据和用户元数据。我们假设:
根据这些假设,我们可以计算出我们需要的存储空间:
我们的系统由以下几个组件组成:
移动应用程序:这是用户与服务交互的前端。用户可以搜索歌曲、播放音乐、创建播放列表等。当用户执行操作(例如播放歌曲)时,应用程序会向后端服务器发送请求。
负载均衡器:这是一个中间层,它将传入的流量分配到多个 Web 服务器上。这提高了我们的应用程序的可用性和容错能力。
Web 服务器 (API) :这是处理来自移动应用程序的请求的 API 层。例如,如果用户想要播放歌曲,请求就会发送到这些网络服务器。然后,服务器确定歌曲所在的位置(在数据库或存储服务中)以及如何检索它。
数据存储将分为两个独立的服务 - 歌曲的 Blob 存储(我们将在其中存储实际的歌曲文件)和 SQL 数据库(我们将在其中存储歌曲和用户元数据)。
歌曲 - Blob 存储:这是一个用于存储实际的歌曲文件的 Blob(二进制大对象)存储服务。这些服务旨在存储大量非结构化数据。我们可以使用一些云服务提供商的 Blob 存储服务,例如 AWS S3、GCP、Azure Blob 存储等。
用户、艺术家和歌曲元数据 — SQL 数据库:这是一个用于存储结构化数据的 SQL 数据库服务。这些数据包括用户信息(如用户名、密码和电子邮件地址)和歌曲元数据(如歌曲名称、艺术家姓名、专辑详细信息等)。我们可以使用一些关系型数据库管理系统,例如 MySQL、PostgreSQL、Oracle 等。
为什么用户、艺术家和歌曲元数据选择用 SQL 数据库?因为 SQL 数据库非常适合此类结构化数据,因为它们允许复杂的查询以及不同类型数据之间的关系。
每个歌曲文件都存储为“blob”,SQL 数据库通常会存储对此文件的访问连接(如 URL)。
我们的表结构设计由以下几个部分组成:
当我们存储结构设计好以后,我们就可以进行播放歌曲的操作了。
当用户想要播放一首歌曲时,移动应用程序会向 Web 服务器发送一个请求,包含歌曲的 ID。Web 服务器会从 SQL 数据库中查询歌曲的元数据,包括 FileURL。然后,Web 服务器会使用 FileURL 从 Blob 存储中获取歌曲文件,并将其逐块流式传输到移动应用程序。
或者我们可以直接将 FileURL 返回给移动应用程序,让它从 Blob 存储中直接下载歌曲文件,从而减少 Web 服务器的负载。
当系统处于规模化阶段,我们假设系统需要处理 5000 万用户和 2 亿首歌曲。我们需要重新计算数据,引入缓存和 CDN,以及扩展数据库。
我们需要重新计算我们需要的存储空间。我们需要将歌曲存储在某种存储中,以及存储歌曲元数据和用户元数据。我们假设:
根据这些假设,我们可以计算出我们需要的存储空间:
接下来,我们的系统架构与初始阶段相比,有以下几个变化。
由于流量增加,我们需要引入缓存和 CDN(如 Cloudfront / Cloudflare)来提供歌曲,并且每个 CDN 在地理位置上都将靠近一个区域。因此它可以比我们原有的 web 服务器更快地提供歌曲。
并且我们可以使用 LRU(最近最少使用)驱逐策略来缓存流行歌曲,不流行的歌曲仍然会从 Blob 存储中获取,然后缓存到 CDN。歌曲文件还可以直接从云存储传输到客户端,这将减少网络服务器的负载。
数据库也需要扩展。由于我们知道我们的应用程序的读取次数多于写入次数,这意味着有很多用户在听歌曲,但上传歌曲的艺术家数量相对较少。
我们可以搭建数据库主从,将用户的读操作和写操作分开,当用户检索歌曲和元数据时请求会到达从数据库,当用户上传歌曲时,请求会到达主数据库。通过读写分离,我们可以提高应用程序的歌曲播放速度。
我们给大家介绍了如何设计一个类似 Spotify 的音乐流媒体系统,从基础版本到规模化阶段,我们都给出了合理的方案和估计。我们的设计具有以下优点:
当然我们的设计也有一些地方没有阐述,大家可以自己研究,
如果觉得这篇文章写的不错的话,不妨点赞加关注,我会更新更多技术干货、项目教学、经验分享的文章。