如何一步一步设计一个大规模复杂的系统

良好的系统设计能力,是一个优秀程序员的必要素质,反应出了处理复杂问题的能力,也是面试过程中能否获得相应的职位和薪酬的关键。

最近在 https://www.educative.io/ 上看到一份介绍系统设计的教程:Grokking the System Design Interview[1],里面有很多系统设计实例,如 Dropbox, Twitter, Facebook Messenger, Uber 等,教程是收费的,质量很高,学习系统设计的绝佳资料,该教程的中文资料很少,这里就将其中的核心内容翻成中文与大家分享,如果想看英文原版,回复「系统设计」即可获取。

许多软件工程师在系统设计面试(以下简称 SDI)时遇到困难,主要有三个方面原因:

  • SDI 具有非结构化性质,往往要求开放式设计,很多问题没有标准答案。

  • 他们缺乏开发大型系统的经验。

  • 他们没有为 SDI 做准备。

像编码面试一样,没有认真准备 SDI 的应聘者,大部分表现不佳,尤其是在 Google,Facebook,Amazon,Microsoft 等顶尖公司的面试,如果表现不及平均水平的候选人,获得获得 offer 的机会非常渺茫。另一方面,良好的面试表现总是会带来更好的回报,或者是更高的职位,或者是更高的薪水,因为这显示了候选人处理复杂系统的能力。

接下来,我们将按以下步骤循序渐进地解决多个设计问题:

第一步:需求澄清

在需求范围内提出一些问题有助于澄清需求。设计问题大多是开放性的,并且没有一个标准答案,这就是为什么要澄清一些具体需求。花费足够时间来定义系统最终目标有助于在面试中获得成功。另外,由于系统设计的面试只有 35-40 分钟的时间,我们应该弄清楚哪些部分需要重点关注。

以设计一个类 Twitter 的服务为例,在开始设计之前应先回答以下问题:

  • 我们服务的用户能否发布推文并关注其他人?

  • 我们是否还应该设计来创建和显示用户的时间轴?

  • 推文中是否包含照片和视频?

  • 我们是仅专注于后端还是前端?

  • 用户将能够搜索推文吗?

  • 我们需要显示热门话题吗?

  • 是否有关于新(或重要)推文的推送通知?

这些问题将决定最终设计的系统长什么样。

第二步:系统接口定义

定义系统期望的接口(API)不仅可以帮助建立预期的接口协议 ,也可以确保我们没有弄错需求。比如类似 Twitter 的服务的接口可能是这样的:

postTweet(user_id, tweet_data, tweet_location, user_location, timestamp, …) 
generateTimeline(user_id, current_time, user_location, …) 
markTweetFavorite(user_id, tweet_id, timestamp, …)

第三步:资源预估

预估我们要设计的系统的规模是非常必要的,有助于我们后续的系统扩展、分区、负载平衡和缓存的设计。

  • 系统预期的规模,例如,新推文的数量,推文的阅读量,每秒产生的时间线?

  • 我们需要多少存储空间?如果用户可以拍摄照片和视频,又需要多少存储空间。

  • 我们期望多大的带宽?这对于决定我们如何管理流量和平衡服务器之间的负载。

第四步:设计数据模型

早一点定义数据模型可以弄明白数据如何在不同组件之间进行流转。数据模型将指导数据分区和管理。设计者应该识别系统的各个实体,它们之间的交互方式以及 数据管理的各个方面,例如存储、传输、加密等。以下是我们的类 Twitter 服务的一些实体:

  • User:UserID, Name, Email, DoB, CreationData, LastLogin 等。

  • Tweet:TweetID,Content,,TweetLocation,NumberOfLikes,TimeStamp等。

  • UserFollowo:UserdID1,UserID2

  • FavoriteTweets: UserID, TweetID, TimeStamp

我们应该使用哪个数据库系统?像 Cassandra 这样的 NoSQL 是否最适合我们的需求,还是应该使用类似于 MySQL 的解决方案?我们应该使用哪种块存储来存储照片和视频?

第五步:高级设计

画一个带有 5-6 个方框的图,代表我们系统的核心组件。我们应该识别出足够的组件来解决端到端的问题,对于 Twitter 而言,我们将需要多个应用服务器来服务所有的读/写服务,并配置负载平衡器进行流量分配。假如读流量大于写流量,我们可以使用单独的服务器进行处理这些情况,比如分配 10 台服务器服务读请求,2 台服务器服务写请求。在后端,我们需要一个高性能的数据库,该数据库可以存储所有推文并支持大量读取。我们还需要一个分布式文件存储系统来存储照片和视频。

第六步:详细设计

深入挖掘两个或三个组成部分;面试官的反馈意见引导我们进一步讨论。我们应该能够提出不同的方法,它们的优点和缺点,并说明为什么我们会选择另一种方法。请记住,没有标准答案,唯一重要的是有限资源前提下怎么做出权衡。

  • 由于我们将存储大量数据,因此如何将数据分区到分发到多个数据库?是否应该尝试将用户的所有数据存储在同一数据库?它会导致什么问题?

  • 如何处理发大量推文或关注很多人的热门用户?

  • 由于用户的时间轴将包含最新推文,为了获取最新推文是否需要优化数据的存取方式?

  • 我们应该在多少层引入缓存以加快处理速度?

  • 哪些组件需要更好的负载平衡?

第七步:找出并解决瓶颈

找出尽可能多的瓶颈问题,并提出缓解这些瓶颈的不同方法。比如:

  • 我们的系统中是存在单点故障?应该采取什么措施缓解这种情况?

  • 我们是否有足够的数据备份,在多少台服务器宕机的情况下仍可以为用户提供服务?

  • 类似的,我们是否有足够数量的不同服务在运行,即使一些服务有故障也不会会导致系统崩溃?

  • 我们如何监控我们的服务性能?关键时刻比如组件发生故障或性能下降时会收到报警吗?

最后的话

简而言之,面试前有足够的准备是系统设计面试成功的关键,上述步骤可以指导我们设计一个复杂的大规模系统,涵盖了的不同方面的面试问题,后续的面试问题,可以参考以上步骤来思考和回答。

PS:你的每一次点赞和在看,都是对我最大的鼓励。  

参考资料

[1]

Grokking the System Design Interview: https://www.educative.io/courses/grokking-the-system-design-interview

 推荐阅读:Django 面试题

有问题,留言讨论

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux虚拟内存管理是操作系统中非常重要的一个部分,下面我将以图形化的方式带您深入理解。 首先,让我们来看一张图示,展示了Linux虚拟内存管理的关键组成部分。在Linux中,虚拟内存主要由三个部分组成:用户空间、内核空间和交换区。 用户空间是应用程序运行的空间,程序的逻辑代码和数据都存放在这里。用户空间的大小是由操作系统分配的,一般是32位或64位。 内核空间是操作系统的核心部分,包含了操作系统的核心代码和数据。内核空间是操作系统独占的,应用程序无法直接访问。 交换区是用于辅助内存管理的一块磁盘空间。当内存不足时,操作系统会将部分内存中的数据写入交换区,从而腾出更多的内存空间。 接下来,我们来详细了解一下虚拟内存管理的过程。当应用程序需要访问内存中的某个数据时,首先会发起一个内存访问请求。操作系统会通过页表将虚拟地址转换为物理地址,从而确定在实际的物理内存中的位置。如果所需的数据已经在物理内存中,则应用程序可以直接访问,否则操作系统就会从交换区中将数据加载到物理内存中,并更新页表。 虚拟内存管理还包括页面置换算法,用于在内存不足时选择哪些页面从内存中换出到交换区中。常见的页面置换算法有最近最少使用(LRU)和先进先出(FIFO)等。 使用虚拟内存管理技术,操作系统可以为每个应用程序提供独立的内存空间,保证了应用程序的隔离性和安全性。虚拟内存管理还可以提供更大的内存空间,使得应用程序能够处理更大规模的数据。 总的来说,Linux虚拟内存管理是一套复杂的技术体系,通过将虚拟地址转换为物理地址,实现了对内存的合理管理和优化。通过使用交换区和页面置换算法,操作系统可以在内存不足时保证系统的正常运行。希望以上图文说明能帮助您更深入地理解Linux虚拟内存管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值