点击上方蓝字关注我们
Spotify 是世界上最受欢迎的音乐流媒体平台,每月活跃 用户 (MAU) 超过 6 亿,付费用户超过 2 亿。在本文中,我们将学习如何设计 像 Spotify 这样的音乐流媒体服务,该服务每天可以处理数以百计的数百万用户和数十亿个音乐流,确保低延迟和高可用性。
1.需求收集
功能需求
在深入研究设计之前,让我们概述一下功能性和非功能性需求。
搜索:用户可以搜索歌曲、艺术家、专辑和播放列表。
音乐流媒体:用户可以实时流式传输歌曲。
播放列表:用户可以创建、共享和修改播放列表。
音乐推荐:用户根据其收听历史记录和偏好接收歌曲推荐。
广告支持模式:免费套餐用户在播放几首歌曲后就会看到广告。
非功能性需求
可扩展性:该系统应处理全球数以百计的数百万用户,并能够同时流式传输数百万首歌曲。
低延迟:实时流式处理必须具有低延迟,才能获得无缝的用户体验。
高可用性:系统必须始终可用,停机时间最短。
全球覆盖:支持来自不同地理区域的用户,可能利用 CDN 更快地提供音频文件。
2. 容量估算
我们假设以量特征:
用户群
总活跃用户数:5 亿
日活跃用户:1 亿
每位用户每天的平均流数:10
歌曲平均大小:5 MB
平均歌曲时长:4 分钟
歌曲目录大小:1 亿首歌曲
网络带宽估算
每日歌曲流 = 100M 用户× 10 首歌曲 = 10 亿次流/天
每天的数据传输量 = 10 亿× 5 MB = 5 PB/天
每秒数据传输量 = 5PB / 86400 = 58GB/秒
存储估算
音乐总存储 = 1 亿首歌曲 × 5 MB/歌曲 = 500 TB
假设每首歌曲有 2 KB 的元数据,每个用户有 10 KB 的元数据(用户详细信息、首选项、播放列表等)
歌曲元数据总存储量 = 1 亿首歌曲× 2 KB = 200 GB
5 亿用户的总存储 = 5 亿× (10KB) = 5TB
缓存估计
缓存在减少存储系统的负载和确保热门内容的低延迟方面发挥着重要作用:
经常播放的歌曲元数据可以缓存在内存中
让我们假设前 20% 的歌曲贡献了 80% 的请求
假设 Spotify 有 1 亿首歌曲,并且前 20% 的歌曲被缓存
缓存大小 = 2000 万首歌曲 × 2KB/首歌曲 = 40GB
3. 高层设计
Spotify 的系统架构可以分为几个高级组件:
3.1 客户端应用程序
客户端应用程序包括 Spotify 的移动、桌面和 Web 版本,它提供了一个干净直观的 UI 来与服务交互。它与用于搜索、流式传输、播放列表和推荐的后端 API 进行通信,并通过在设备上缓存音乐(下载的内容)来支持离线收听。
3.2 负载均衡
Load Balancer 是所有客户端请求的入口点。它将传入的客户端请求均匀地分布在后端服务的多个实例之间,从而防止任何单个服务器过载。
3.3 应用服务器
接收来自负载均衡器的传入请求,并将请求重定向到相应的服务。
3.4 服务
流媒体服务:实时处理从存储系统到用户设备的音乐流媒体。
搜索服务:处理歌曲、艺术家、专辑和播放列表的搜索。
推荐服务:根据用户行为(如收听历史记录、点赞和播放列表创建)提供个性化音乐推荐。
广告服务:处理免费套餐用户的广告投放。
用户服务:存储和管理用户配置文件,包括个人信息、订阅类型和首选项。管理用户播放列表,允许用户创建、修改和共享它们。
3.5 储存
数据库:存储用户配置文件、播放列表、歌曲元数据和搜索索引。
Blob 存储:用于处理音频和广告文件的大规模存储的分布式存储系统(例如 AWS S3)。
内容分发网络 (CDN):用于以最小的延迟高效地向全球用户交付大型音频文件。
缓存:缓存经常访问的数据,例如流行歌曲和推荐,以提高性能并减少存储和数据库系统的负载。
3.6 分析服务
分析和监控服务跟踪用户参与度、系统性能并记录系统运行状况。
它会在检测到问题时生成警报,并记录所有系统活动以进行故障排除。
4. 数据库设计
以下是我们需要存储在数据库中的关键实体:
Users, Songs, Artists, Albums, Playlists, Streams, Search Index and Recommendations.
鉴于数据类型的多样性和高查询需求,我们结合使用了关系数据库、NoSQL 数据库和分布式存储系统。
4.1 结构化数据的关系数据库
要存储高度结构化的数据,如用户配置文件、播放列表、歌曲元数据、艺术家和专辑,我们可以使用 PostgreSQL 或 MySQL 等关系数据库。
计划类型(免费、高级、家庭等)。
存储中歌曲文件的 URL(例如 AWS S3)。
歌曲的长度(以秒为单位)。
4.2 用于非结构化数据的 NoSQL 数据库
要存储非结构化和半结构化数据,我们可以使用 MongoDB、Cassandra 或 DynamoDB 等 NoSQL 数据库。NoSQL 数据库提供灵活性和可扩展性,使其成为处理高度动态数据(如推荐和搜索索引)的理想选择。
推荐表
Spotify 根据用户的收听行为为用户生成推荐,并且这些数据会经常更新。示例:
搜索索引
搜索索引存储在 Elasticsearch 等 NoSQL 数据库中 ,以便跨歌曲、艺术家和专辑进行快速、模糊的搜索查询。这些索引会随着新内容的添加而不断更新。示例
4.3 分布式存储系统
要存储大量音频和广告文件,我们可以使用像 AWS S3 这样的分布式存储系统。S3 可确保高持久性和可用性,使其成为处理大型静态文件的理想存储解决方案。
示例存储对象:
文件: https://s3.amazonaws.com/spotify/songs/blinding_lights.mp3
元数据:文件大小:4 MB,比特率:128 kbps,格式:MP3
4.4 内容分发网络 (CDN)
我们使用内容分发网络 (CDN) 以最小的延迟将大型音频文件(歌曲)分发给全球用户。通过从 CDN 边缘服务器提供音乐,Spotify 确保 为世界各地的用户提供低延迟的音乐流媒体体验,最大限度地减少缓冲时间并减少中央存储系统的负载。原始音乐文件存储在分布式存储系统(例如 AWS S3)中。CDN 在首次请求歌曲时从此源存储中提取歌曲,并将其缓存以供将来请求。
4.5 缓存层
缓存经常访问的数据(如用户首选项、流行歌曲或推荐)可以提高性能。可以使用 Redis 等缓存层 来临时存储此数据。
缓存数据示例:
搜索查询:缓存热门搜索查询,以避免重复命中搜索索引。
流行歌曲:可以缓存经常流式传输的歌曲以减少数据库查询。
用户首选项:将用户喜欢的歌曲和播放列表存储在缓存中,以便更快地检索。
示例 — 缓存中用户首选项的 SET/GET 查询:
SET user:preferences:12345 "{liked_songs: [1, 2, 3], playlists: [10, 11, 12]}"
GET user:preferences:12345
SET user:preferences:12345 "{liked_songs: [1, 2, 3], playlists: [10, 11, 12]}"
GET user:preferences:12345
4.6 分析和监控数据(数据仓库)
分析和监控数据对于跟踪用户参与度、系统性能和识别潜在问题至关重要。数据在数据仓库或分布式数据存储(例如 Hadoop、BigQuery)中聚合和处理。
Analytics 的主要用例:
用户参与度:有关流、跳过和播放列表添加的数据用于生成对用户行为的见解。
系统监控:来自各种服务的日志用于监控系统运行状况、检测异常和执行性能调整。
版税计算:流数据用于根据歌曲播放量和地理覆盖范围计算艺术家的付款。
流日志示例:
5. 系统 API 设计
我们将设计直观、高效且可扩展的 RESTful API。让我们将 API 设计分解为几个关键端点:
5.1 搜索 API
搜索 API 允许用户搜索歌曲、艺术家、专辑或播放列表。搜索结果根据相关性、受欢迎程度和用户偏好进行排名。
Endpoints 端点GET /search
查询参数:
query
:搜索词(例如,“Blinding Lights”)
type
:要搜索的资源类型(歌曲
、艺术家
、专辑
、播放列表
)limit
:要返回的最大结果数(默认值:20)
offset
:用于分页(默认值:0)
Response: 响应:
{ "results": [ { "type": "song", "id": "12345", "title": "Blinding Lights", "artist": "The Weeknd", "album": "After Hours" }, { "type": "artist", "id": "67890", "name": "The Weeknd" } ] }
5.2 音乐流媒体 API
Streaming API 处理音乐文件从后端或 CDN 到用户设备的交付。
Endpoint
GET /stream/{song_id}
响应:
HTTP 302 重定向到托管歌曲的 CDN URL:
{
"url": "https://cdn.spotify.com/song/12345"
}
5.3 推荐 API
Recommendations API 根据用户的收听历史记录、偏好和喜好提供个性化的歌曲建议.
端点
GET /recommendations/{user_id}
查询参数:
limit
:要返回的建议数(默认值:10)。
响应:
{
"recommendations": [
{
"song_id": "12345",
"title": "Blinding Lights",
"artist": "The Weeknd",
"score": 0.98
},
{
"song_id": "67890",
"title": "Can't Feel My Face",
"artist": "The Weeknd",
"score": 0.95
}
]
}
5.4 广告投放 API
对于免费套餐用户,Spotify 将广告注入他们的收听体验中。Ad Delivery API 根据用户偏好和人口统计数据获取和提供个性化广告。
端点
获取 /ads/{user_id}
获取要在音乐流式传输期间播放的用户广告。
响应:
{
"ad_id": "ad12345",
"ad_url": "https://cdn.spotify.com/ads/ad12345.mp3",
"duration": 30
}
6. 深入研究关键组件
6.1 音乐流媒体服务
流媒体服务是 Spotify 架构的核心,负责高效、安全、可靠地实时向数百万用户交付音乐内容。音乐文件的实际交付由内容分发网络(Cloudflare、AWS CloudFront)管理。这可确保从靠近用户的地理分布式服务器提供音乐,从而最大限度地减少延迟和带宽消耗。
请求工作流
客户端发送流式处理请求(例如 /stream/{song_id}
)。
App 服务器对用户进行鉴权,并将请求路由到 Streaming Service。
如果歌曲不在 CDN 中,则流式处理服务会检索音频文件的位置(从 Blob 存储中)并将文件推送到最近的 CDN 边缘服务器。CDN 返回指向流式处理服务的 URL 以流式传输音频。
CDN URL 将返回给客户端,允许客户端流式传输音频。
6.2 推荐服务
推荐系统分析用户的收听习惯、喜欢和播放列表。它结合使用了协作筛选(基于具有相似偏好的用户)和基于内容的筛选(基于歌曲元数据)。
协同过滤
协同过滤是推荐系统中最常用的技术之一。此方法利用具有相似音乐品味的用户的行为来生成推荐。
基于用户的协作过滤:此技术根据用户的收听历史记录对用户进行分组。例如,如果用户 A 和用户 B 都经常收听同一组艺术家和歌曲,则系统可能会推荐用户 A 听过但用户 B 没有听过的歌曲。
基于ITEM的协作过滤:在这种技术中,根据歌曲与用户之前喜欢的歌曲的相似性来推荐歌曲。如果很多喜欢歌曲 X 的用户也喜欢歌曲 Y,系统会将歌曲 Y 推荐给听过歌曲 X 的用户。
基于内容的筛选
基于内容的筛选侧重于歌曲的属性,例如流派、艺术家、专辑、速度和乐器,以向用户推荐类似的歌曲。
歌曲属性:Spotify 收集每首歌曲的元数据,包括流派、节奏、情绪和乐器。此数据用于推荐与用户已经喜欢或收听的歌曲具有相似属性的歌曲。
音乐人相似度:如果用户聆听了特定音乐人的多首歌曲,系统可能会根据共同的属性(例如流派、风格)推荐类似音乐人的歌曲。
6.3 搜索服务
Spotify 中的搜索服务允许用户有效地从庞大的目录中查找歌曲、艺术家、专辑、播放列表和播客。Search Service 的体系结构可以分为以下关键组件:
Query Parser:解释和规范化用户的搜索查询。
搜索索引:动态更新的索引,包含所有歌曲、艺术家、专辑和播放列表的元数据。可以使用 Elasticsearch 或 Apache Solr 等搜索引擎来构建和管理此索引。
排名引擎:搜索索引返回匹配结果后,排名引擎会对结果进行排序,以确保最相关的结果显示在顶部。
个性化层:根据用户的收听历史记录、偏好和人口统计信息自定义搜索结果。
搜索自动完成:在用户键入查询时为用户提供建议,从而加快搜索过程。
Cache Layer:缓存经常搜索的查询,以提高性能并减少后端的负载。
搜索索引更新器:确保搜索索引与添加到 Spotify 目录中的新内容保持同步。
7. 解决关键问题和瓶颈
7.1 可扩展性
分片:要扩展 SQL 数据库并均匀分配负载,请对用户、播放列表和歌曲元数据等大型表实施分片。
索引:在 user_id 和 playlist_id 等经常访问的字段上添加索引以提高查询性能。
分区:NoSQL 数据库可以使用分区策略将数据分布到多个节点,从而确保即使在大规模情况下也能实现低延迟访问。
TTL (Time to Live):为缓存数据提供 TTL,以确保过时的数据定期失效。
7.2 可靠性
为了确保高可用性,Spotify 应实施容错系统:
复制的数据库:跨多个数据中心复制用户、歌曲和播放列表数据,以防止数据丢失。
缓存复制:Redis 可以配置为跨多个实例复制缓存数据,以实现容错。
自动扩展:根据流量负载自动扩展服务器数量。
正常故障转移:如果一台服务器发生故障,流量将重新路由到另一台服务器,而不会中断服务。
监控和警报:实施全面的监控和警报系统。
7.3 用户安全
Spotify 处理敏感数据,例如用户个人资料和支付信息。
身份验证:使用 OAuth 2.0 进行安全的用户身份验证。
加密:加密所有传输中和静态的敏感数据。
Rate Limiting:对用户进行速率限制,以确保来自单个客户端的过多 API 请求受到限制,以保护系统。
数据隐私:实施强大的访问控制,以确保用户数据不被泄露或滥用。