需求和目标
功能要求:
用户应该能够上传视频。
用户应该能够共享和查看视频。
用户可以根据视频标题执行搜索。
我们的服务应该能够记录视频的统计数据,例如喜欢/不喜欢,观看总数等。
用户应该能够添加和查看视频评论。
非功能性要求:
系统应该高度可靠,任何上传的视频都不应丢失。
该系统应该是高度可用的。 一致性可能会受到影响(为了可用性),如果用户暂时没有看到视频,那应该没问题。
用户在观看视频时应具有实时体验,不应感到任何滞后。
不在范围内:视频推荐,最受欢迎的视频,频道和订阅,以后观看,收藏等。
容量估计和限制
我们假设我们有15亿用户,其中8亿是每日活跃用户。平均而言,如果用户每天观看五个视频,则每秒的总视频观看数为:
800M * 5/86400秒=> 46K视频/秒
我们假设我们的上传:观看比率是1:200,即,对于每个视频上传,我们观看了200个视频,每秒上传230个视频。
46K / 200 => 230视频/秒
存储估算:让我们假设每分钟500小时的视频上传到Youtube。平均而言,一分钟的视频需要50MB的存储空间(视频需要以多种格式存储),一分钟内上传的视频所需的总存储空间为:
500小时* 60分钟* 50MB => 1500 GB /分钟(25 GB /秒)
估计这些数字,忽略视频压缩和复制,这将改变我们的估计。
带宽估算:每分钟上传500小时的视频,假设每个视频上传的带宽为10MB /分钟,我们每分钟就会获得300GB的上传量。
500小时* 60分钟* 10MB => 300GB /分钟(5GB /秒)
假设上传:视图比率为1:200,我们需要1TB / s的传出带宽。
系统API
uploadVideo(api_dev_key, video_title, vide_description, tags[], category_id, default_language,
recording_details, video_contents)
参数:
api_dev_key(string):已注册帐户的API开发人员密钥。 除其他外,这将用于根据用户分配的配额限制用户。
video_title(string):视频的标题。
vide_description(string):视频的可选描述。
tags(string []):视频的可选标签。
category_id(string):视频的类别,例如电影,歌曲,人物等。
default_language(string):例如英语,普通话,印地语等。
recording_details(string):录制视频的位置。
video_contents(stream):要上传的视频。
返回:(字符串)
成功上传将返回HTTP 202(请求已接受),并且一旦视频编码完成,则通过电子邮件通知用户访问视频的链接。 我们还可以公开可查询的API,以便用户了解其上传视频的当前状态。
searchVideo(api_dev_key, search_query, user_location, maximum_videos_to_return, page_token)
参数:
api_dev_key(string):我们服务的注册帐户的API开发人员密钥。
search_query(string):包含搜索词的字符串。
user_location(string):执行搜索的用户的可选位置。
maximum_videos_to_return(number):一个请求中返回的最大结果数。
page_token(string):此标记将指定应返回的结果集中的页面。
返回:(JSON)
包含与搜索查询匹配的视频资源列表的信息的JSON。 每个视频资源都有一个视频标题,一个缩略图,一个视频创建日期以及它有多少视图。
高层设计
1.处理队列:每个上传的视频都将被推送到处理队列,稍后将进行排队以进行编码,缩略图生成和存储。
2.编码器:将每个上传的视频编码为多种格式。
3.缩略图生成器:我们需要为每个视频添加一些缩略图。
4.视频和缩略图存储:我们需要将视频和缩略图文件存储在某些分布式文件存储中。
5.用户数据库:我们需要一些数据库来存储用户的信息,例如姓名,电子邮件,地址等。
6.视频元数据存储:元数据数据库将存储有关视频的所有信息,如标题,系统中的文件路径,上传用户,总观看次数,喜欢,不喜欢等。此外,它还将用于存储所有视频评论。
数据库SCHEMA
视频表
评论表
用户表
UserID, Name, email, address, age, registration details
细节设计
该服务阅读量很大,因此我们将专注于构建一个可以快速检索视频的系统。我们可以预期我们的读取:写入比率为200:1,这意味着对于每个视频上传,有200个视频视图。
视频存储在哪里?视频可以存储在分布式文件存储系统中,如HDFS或GlusterFS。
我们应该如何有效地管理读取流量?我们应该将读取流量与写入隔离开来。由于我们将拥有每个视频的多个副本,因此我们可以在不同的服务器上分配读取流量。对于元数据,我们可以使用主从配置。这样的配置可能导致数据的某些陈旧性,例如,当添加新视频时,其元数据将首先插入到主设备中,然后在从设备上重播之前,我们的从设备将无法看到它,因此会将过时的结果返回给用户。这种陈旧性在我们的系统中可能是可以接受的,因为它会非常短暂,并且用户将能够在几毫秒后看到新视频。
缩略图存放在哪里?将有比视频更多的缩略图。如果我们假设每个视频都有五个缩略图,我们需要一个非常高效的存储系统,可以提供巨大的读取流量。在决定将哪个存储系统用于缩略图之前,将有两个考虑因素:
缩略图是小文件,每个最大5KB。
与视频相比,读取缩略图的流量将是巨大的。用户将一次观看一个视频,但他们可能正在查看包含20个其他视频缩略图的页面。
让我们评估将所有缩略图存储在磁盘上。鉴于我们有大量的文件;要读取这些文件,我们必须对磁盘上的不同位置执行大量搜索。这是非常低效的并且将导致更高的延迟。
Bigtable在这里是一个合理的选择,因为它将多个文件组合成一个块以存储在磁盘上,并且非常有效地读取少量数据。这两项都是我们服务的两大要求。将热缩略图保留在缓存中也有助于改善延迟,并且鉴于缩略图文件很小,我们可以轻松地将大量此类文件缓存在内存中。
视频上传:由于视频可能很大,如果在上传时连接断开,我们应该支持从同一点恢复。
视频编码:新上传的视频存储在服务器上,新的任务被添加到处理队列中,以将视频编码为多种格式。一旦完成所有编码;上传者会收到通知,视频可供观看/分享。
元数据分区
由于我们每天都有大量的新视频,而且我们的读取负载也非常高,我们需要将数据分发到多台机器上,以便我们可以高效地执行读/写操作。我们有很多选项来分析我们的数据。让我们逐个分析这些数据的不同策略:
基于UserID的分片:我们可以尝试在一台服务器上存储特定用户的所有数据。在存储时,我们可以将UserID传递给我们的哈希函数,该函数将用户映射到数据库服务器,我们将存储该用户视频的所有元数据。在查询用户的视频时,我们可以要求我们的哈希函数找到保存用户数据的服务器,然后从那里读取它。要按标题搜索视频,我们必须查询所有服务器,每个服务器都会返回一组视频。然后,集中式服务器将这些结果聚合并排序,然后再将它们返回给用户。
这种方法有几个问题:
如果用户变得流行怎么办?持有该用户的服务器上可能存在大量查询,从而产生性能瓶颈。这将影响我们服务的整体表现。
随着时间的推移,与其他用户相比,一些用户最终可能会存储大量视频。保持不断增长的用户数据的均匀分布是非常困难的。
要从这些情况中恢复,我们必须重新分配/重新分配我们的数据或使用一致性散列来平衡服务器之间的负载。
基于VideoID的分片:我们的散列函数会将每个VideoID映射到随机服务器,我们将存储该视频的元数据。要查找用户的视频,我们将查询所有服务器,每个服务器将返回一组视频。集中式服务器将在将这些结果返回给用户之前对其进行聚合和排名。这种方法解决了我们的热门用户问题,但将其转移到热门视频。
我们可以通过引入缓存来在数据库服务器前存储热门视频来进一步提高性能。
视频去重
由于用户数量庞大,上传大量视频数据,我们的服务将不得不处理广泛的视频复制。重复视频的宽高比或编码通常不同,可以包含叠加或其他边框,也可以是较长的原始视频的摘录。重复视频的激增会对许多层面产生影响:
数据存储:我们可能通过保留同一视频的多个副本来浪费存储空间。
缓存:重复的视频会占用可用于唯一内容的空间,从而导致缓存效率降低。
网络使用:增加必须通过网络发送到网络内缓存系统的数据量。
能耗:更高的存储容量,低效的缓存和网络使用将导致能源浪费。
对于最终用户,这些低效率将以重复搜索结果,更长的视频启动时间和中断的流式传输的形式实现。
对于我们的服务,当用户上传视频时,重复数据删除最有意义;与后期处理相比,以便稍后查找重复的视频。内联重复数据删除将为我们节省大量资源,可用于编码,传输和存储视频的副本。一旦任何用户开始上传视频,我们的服务就可以运行视频匹配算法(例如,块匹配,相位相关等)以查找重复。如果我们已经上传了视频的副本,我们可以停止上传并使用现有副本,也可以使用新上传的视频(如果质量更高)。如果新上传的视频是现有视频的子部分,反之亦然,我们可以智能地将视频划分为更小的块,以便我们只上传那些丢失的部分。