系统架构设计之解析:内容分享系统案例深度解析

系统架构设计之解析:内容分享系统案例深度解析,在数字时代,内容分享平台成为人们生活中的重要一环,从分享生活点滴、表达情感,到提供信息和娱乐,这类平台已经深深影响了我们的生活。其中,国外Instagram和国内的LOFTER,作为优秀的内容分享平台,凭借其出色的用户体验和强大的功能,吸引了大量的用户。本文将针对LOFTER的系统架构进行深度解析,为我们提供一种理解和设计大规模内容分享系统的视角。,系统架构设计之解析:内容分享系统案例深度解析,项目简介:LOFTER是由网易公司开发的一个内容创作和分享平台,用户可以创建自己的博客,分享文字、图片、音乐等内容。该平台受到创意人群,如摄影爱好者、插画家等的喜爱,提供自由的创作空间。用户可以关注其他博主,互相评论和点赞,通过标签系统找到自己感兴趣的内容。LOFTER也会定期举办各种创作活动,鼓励用户创作和分享。,现在让我们设计一个类似于LOFTER的内容分享服务,用户可以上传照片与其他用户共享。,类似的产品有:Instagram、Picasa,系统难度等级:中等,LOFTER是一个社交网络服务,使其用户能够上传并与其他用户分享他们的照片和视频。LOFTER用户可以选择公开或私密地分享信息。公开分享的任何内容都可以被任何其他用户看到,而私密分享的内容只能被指定的一组人访问。LOFTER还使其用户能够通过许多其他社交网络平台分享,例如微博、Twitter、Flickr和Tumblr。,我们计划设计一个LOFTER的简化版本来解决这个设计问题,用户可以分享照片并关注其他用户。每个用户的“新鲜事”将包括用户关注的所有人的热门照片。,在设计LOFTER时,我们将关注以下一组需求:,功能需求:,非功能性需求:,不在讨论范围内:给照片添加标签,根据标签搜索照片,评论照片,给照片标记用户,推荐关注等等。,该系统将是读取密集型的,因此我们将专注于构建一个可以快速检索照片的系统。,让我们假设我们总共有5亿个用户,每天有100万活跃用户。,每天有200万新照片,每秒23张新照片。,平均照片文件大小=> 200KB,1天的照片所需的总空间,10年所需的总空间:,在高层次上,我们需要支持两种情况,一是上传照片,另一是查看/搜索照片。我们的服务将需要一些对象存储服务器来存储照片,以及一些数据库服务器来存储关于照片的元数据信息。,系统架构设计之解析:内容分享系统案例深度解析,我们需要存储关于用户、他们上传的照片以及他们关注的人的数据。Photo表将存储与照片相关的所有数据;我们需要在(PhotoID,CreationDate)上建立索引,因为我们需要先获取最新的照片。,系统架构设计之解析:内容分享系统案例深度解析,存储上述模式的直接方法是使用像MySQL这样的关系型数据库,因为我们需要联接。但是,关系型数据库带来了他们的挑战,尤其是当我们需要扩展它们的时候。有关详细信息,请参阅 ‘SQL vs. NoSQL’ 章节。,我们可以在分布式文件存储,如HDFS或S3中存储照片。,我们可以在分布式键值存储中存储上述模式,以享受NoSQL提供的好处。所有与照片相关的元数据都可以放到一个表中,其中 ‘key’ 将是 ‘PhotoID’,’value’ 将是一个包含 PhotoLocation, UserLocation, CreationTimestamp 等的对象。,一般来说,NoSQL存储总是维护一定数量的副本以提供可靠性。此外,在这样的数据存储中,删除操作并不会立即执行;系统会保留数据若干天(支持撤销删除)后才会从系统中永久删除。,让我们估计一下每个表中将要存入多少数据,以及我们在10年内需要多少总存储空间。,用户:假设每个 “int” 和 “dateTime” 是四字节,用户表中的每一行将是68字节:,如果我们有5亿用户,我们将需要32GB的总存储空间。,5亿 * 68 ~= 32GB,Photo:Photo 表中的每一行将是284字节:,如果每天有200万新照片被上传,我们将需要0.5GB的存储空间:,10年我们将需要1.88TB的存储空间。,UserFollow:UserFollow表中的每一行将占用8字节。如果我们有5亿用户,每个用户平均关注500个用户。我们将需要1.82TB的存储空间用于UserFollow表:,5亿用户 * 500关注者 * 8字节 ~= 1.82TB,10年内所有表所需的总空间将为3.7TB:,32GB+1.88TB+1.82TB =3.7TB,照片上传(或写入)可能会比较慢,因为它们需要写入磁盘,而读取则会更快,尤其是当它们由缓存服务时。,上传的用户可能会占用所有可用的连接,因为上传是一个慢的过程。这意味着如果系统忙于处理所有的 ‘写’ 请求,那么 ‘读’ 请求就无法得到服务。在设计我们的系统时,我们应该记住网络服务器在连接数量上有一个限制。如果我们假设一个网络服务器在任何时候最多可以有500个连接,那么它不能同时有超过500个的上传或读取。为了处理这个瓶颈,我们可以将读取和写入分开成为独立的服务。我们将有专门的服务器用于读取,不同的服务器用于写入,以确保上传不会拖慢系统。,将照片的读取和写入请求分开也将允许我们独立地对这些操作进行扩展和优化。,系统架构设计之解析:内容分享系统案例深度解析,丢失文件对我们的服务来说是不可接受的。因此,我们将存储每个文件的多个副本,这样即使一个存储服务器出现问题,我们也可以从另一个存储在不同存储服务器上的副本中取回照片。,这个原则同样适用于系统的其他组件。如果我们想要系统具有高可用性,我们需要在系统中运行多个服务的副本,这样即使有几个服务出现问题,系统仍然可以保持可用和运行。冗余消除了系统中的单点故障。,如果任何时刻只需要运行一个服务的实例,我们可以运行一个冗余的次要副本,它不服务任何流量,但当主服务有问题时,它可以在故障切换后接管控制。,在系统中创建冗余可以消除单点故障,并在危机时提供备用或备用功能。例如,如果有两个相同服务的实例在生产环境中运行,其中一个失败或降级,系统可以切换到健康的副本。故障切换可以自动发生,也可以需要手动干预。,系统架构设计之解析:内容分享系统案例深度解析,我们来讨论一下元数据分片的不同方案:,假设我们根据’UserID ‘进行分片,以便我们可以将一个用户的所有照片保留在同一个分片上。如果一个数据库分片是1TB,我们需要4个分片来存储3.7TB的数据。假设为了更好的性能和可扩展性,我们保留10个分片。,因此,我们可以通过UserID % 10找到分片编号,然后在那里存储数据。为了在我们的系统中唯一标识任何照片,我们可以在每个PhotoID后附加分片编号。,我们如何生成PhotoID呢?每个数据库分片都可以有自己的PhotoID自增序列,而且由于我们将ShardID 附加到每个PhotoID上,因此它在我们的整个系统中都将是唯一的。,这种分区方案有哪些问题呢?,如果我们可以先生成唯一的PhotoID,然后通过“PhotoID % 10”找到分片编号,以上的问题就可以解决了。在这种情况下,我们不需要在PhotoID后附加ShardID,因为PhotoID本身在整个系统中都是唯一的。,我们如何生成PhotoID呢?在这里,我们不能在每个分片中定义一个自增序列来定义PhotoID,因为我们需要先知道PhotoID才能找到它将被存储的分片。一个解决方案可能是我们专门分配一个数据库实例来生成自增ID。如果我们的PhotoID可以适应64位,我们可以定义一个只包含一个64位ID字段的表。所以每当我们想在我们的系统中添加一张照片时,我们可以在这个表中插入一个新行,并取那个ID作为新照片的PhotoID。,这个生成键的数据库不会成为单点故障吗?是的,它会。一个解决方法可能是定义两个这样的数据库,一个生成偶数ID,另一个生成奇数ID。对于MySQL,以下脚本可以定义这样的序列:,我们可以在这两个数据库前面放一个负载均衡器,用来在它们之间轮询,并处理宕机问题。这两个服务器可能会失去同步,一个生成的键可能比另一个多,但这不会在我们的系统中造成任何问题。我们可以通过为用户、照片评论或系统中的其他对象定义独立的ID表来扩展这种设计。,另外,我们可以实施一种类似于我们在“设计像TinyURL这样的URL缩短服务”中讨论的’键’生成方案。,我们如何为我们系统的未来增长做计划?我们可以有大量的逻辑分区以适应未来的数据增长,这样一开始,多个逻辑分区就可以驻留在一个物理数据库服务器上。由于每个数据库服务器可以运行多个数据库实例,所以我们可以在任何服务器上为每个逻辑分区有单独的数据库。所以,每当我们觉得某个数据库服务器的数据量很大时,我们可以将一些逻辑分区从它迁移到另一个服务器。我们可以维护一个配置文件(或一个单独的数据库),它可以映射我们的逻辑分区到数据库服务器;这将使我们能够轻松地移动分区。每当我们想移动一个分区时,我们只需要更新配置文件来宣布改变。,为任何给定的用户创建News-Feed,我们需要获取用户关注的人发布的最新的、最受欢迎的、和相关的照片。,为了简化,让我们假设我们需要为用户的News-Feed获取最热门的100张照片。我们的应用服务器首先会获取用户关注的人的列表,然后获取每个用户最新100张照片的元数据信息。在最后一步,服务器会将所有这些照片提交给我们的排名算法,该算法会基于最近性、喜欢程度等因素确定最热门的100张照片,并返回给用户。这种方法的一个可能问题是由于我们必须查询多个表并对结果进行排序/合并/排名,所以延迟可能会更高。为了提高效率,我们可以预先生成News-Feed并将其存储在一个单独的表中。,预先生成新闻动态:我们可以有专门的服务器持续生成用户的News-Feed,并将其存储在“UserNewsFeed”表中。所以每当任何用户需要他们的News-Feed的最新照片时,我们只需要查询这个表并将结果返回给用户。,每当这些服务器需要生成用户的News-Feed时,它们首先会查询UserNewsFeed表,找出上一次为该用户生成News-Feed的时间。然后,从那个时间点开始生成新的News-Feed数据(按照上述步骤)。,将News-Feed内容发送给用户有哪些不同的方法?,关于News-Feed生成的详细讨论,请参阅“设计Facebook的News-Feed”。,创建任何给定用户的News-Feed的一个最重要的需求是获取用户关注的所有人发布的最新照片。为此,我们需要有一种机制来根据照片的创建时间进行排序。为了有效地做到这一点,我们可以将照片的创建时间作为PhotoID的一部分。由于我们在PhotoID上有一个主索引,所以找到最新的PhotoID将会非常快。,我们可以使用纪元时间来实现这一点。假设我们的PhotoID有两部分;第一部分将表示纪元时间,第二部分将是一个自动递增的序列。因此,要生成新的PhotoID,我们可以取当前的纪元时间并追加我们的键生成数据库的自动递增ID。我们可以从这个PhotoID中找出碎片号(PhotoID % 10)并在那里存储照片。,我们的PhotoID的大小可能是多少呢?假设我们的纪元时间从今天开始;我们需要多少位来存储接下来50年的秒数?,86400 秒/天 * 365 (days a year) * 50 (years) => 16亿秒,我们需要 31 位来存储这个数字。由于平均而言,我们预计每秒 23 张新照片,因此我们可以分配 9 个额外位来存储自动递增序列。所以每一秒,我们都可以存储(29≤5122^9leq51229≤512)新照片。我们为序列号分配了 9 位,这超出了我们的要求;我们这样做是为了获得完整的字节数(如40bits=5bytes)。我们可以每秒重置自动递增序列。,我们将在“设计 Twitter”中的“数据分片”下讨论这项技术。,我们的服务将需要一个大规模的照片传递系统来服务全球分布的用户。我们的服务应该使用大量地理分布的照片缓存服务器和CDN(详见我的另一篇关于“Caching”的详细介绍)将其内容推送到用户附近。,我们可以为元数据服务器引入一个缓存,以缓存热门数据库行。我们可以使用Memcache来缓存数据,应用服务器在访问数据库之前可以快速检查缓存是否有所需的行。最近最少使用(LRU)可以是我们系统的合理缓存淘汰策略。根据这种策略,我们首先丢弃最近最少查看的行。,我们如何构建一个更智能的缓存?如果我们遵循二八定则,即每日照片的20%阅读量产生了80%的流量,这意味着某些照片非常受欢迎,大多数人都会阅读。这就决定了我们可以尝试缓存20%的每日照片和元数据的阅读量。

文章版权声明

 1 原创文章作者:cmcc,如若转载,请注明出处: https://www.52hwl.com/28130.html

 2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈

 3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)

 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年6月23日
下一篇 2023年7月15日