Grokking the System Design Interview 系统设计面试:一步步得指导 – (一)起步

3,507 阅读6分钟

Grokking the System Design Interview 系统设计面试:一步步得指导 – (一)起步

翻译自 Grokking the System Design Interview

许多软件工程师,在系统设计面试(SDIs)是会很头疼,主要有三个原因:

  • SDIs 的非固定性,面试者往往被要求处理一个开放性、没有标准答案的问题;
  • 缺少大型系统的开发经验;
  • 没有为系统设计面试做好充分的准备。

和编写面试代码一样,没有特地为 SDI 做准备的面试者大多表现不佳。尤其是在谷歌、Facebook、亚马逊和微软这些顶级公司。在这些公司的面试,如果没有表现得超过平均水平,就很少有机会能够拿到 offer 了。另一方面,表现得出色往往能拿到一个更好的 offer(更高的职位和更多的薪资),因为这展示了面试者应付复杂系统的能力。

在本节课程中,我们将一步步地解决多个系统设计问题。首先来浏览一下步骤:

第一步:明确需求

首先询问我们要解决的问题的确切范围总是好的。系统设计的问题大多是开放的,没有标准的正确答案,因此在面试中对模糊的问题没有明确是很危险的。

愿意花多一些时间来明确系统的目标的候选人是更有可能在面试中成功的。一次面试中往往只有 35 到 40 分钟留给我们来设计一个复杂的系统,所以需要明确系统的哪一部分是需要更加关注的。

让我们根据一个实际案例来进行扩展–设计一个类似「推特」的系统。在开始下一步之前,这里有一些设计「推特」相关的问题需要回答:

  • 我们系统的用户可以发布推文或者关注其他用户吗?
  • 需要创建或者展示用户的时间线吗?
  • 推文可能包含图片和视频吗?
  • 我们只关注后端开发,还是说前端也要呢?
  • 用户可以搜索推文吗?
  • 需要展示热门话题吗?
  • 对于新的(或者重要的)推文,需要有推送能力吗?

诸如此类的问题将会决定我们的系统设计会是什么样子的。

第二步:系统接口定义

定义系统的 API 接口。这个过程不仅仅明确了系统需要的交互方式,同时也确保了需求没有被错误的理解。「推特」服务的 API 接口示例如下:

postTweet(user_id, tweet_data, tweet_location, user_location, timestamp, ...) // 发布推文
generateTimeline(user_id, current_time, user_location, ...) // 用户时间线
markTweetFavorite(user_id, tweet_id, timestamp, ...) // 喜欢推文

第三步:预估背后的资源

评估将要设计系统的规模总是一个好主意。同时也将为后面要做的事情提供帮助,比如扩展、分区、负载均衡以及缓存等。

  • 系统预期的规模是多少(比如推文的产生量、推文的展示量、时间线的生成速度)
  • 需要多少存储?如果推文包含图片和视频,这个数字可能会有很大差异
  • 需要多少网络带宽?在控管理流量和负载均衡的过程中,这会是至关重要的因素。

第四步:定义数据模型

尽早的定义数据模型将会有助于明确数据在系统的多个组件内的传递方式。稍后,这将知道如何做数据分区和治理。候选人需要能够识别系统中的不同实体,这些实体之间怎么互相影响以及跟这些实体相关的各个方面,如存储、传输、加密等。下面这些是类似「Tiwtter」系统服务的一些实体:

User: UserID, Name, Email, DoB, CreationData, LastLogin, etc.

Tweet: TweetID, Content, TweetLocation, NumberOfLikes, TimeStamp, etc. UserFollowo: UserdID1, UserID2 FavoriteTweets: UserID, TweetID, TimeStamp

我们要选择使用哪种数据库?像是 Cassandra 这种 NoSQL 是不是最能满足我们的需求呢?还是要选择类似 MySQL 的关系型数据库呢?要使用哪种类型的块存储方案来存储图片和视频呢?

第五步:上层设计

画 5-6 个方框来展示系统的核心组件。我们需要明确需要哪些组件来端到端得解决实际的问题。
对于 Twitter,需要很多应用服务来应对所有的读写请求,同时在这些服务之前需要负载均衡。如果假定有更多的读请求(相对于写请求来说),可以设计很多分离的副本服务。在后端,需要一个高效的数据库库,可以存下所有的推文,同时可以支持大量的读取。还需要一个分布式的文件存储系统来存储图像和视频。

第六步:细节设计

深入研究其中的两到三个组件,面试官的反馈总是可以引导我们系统的哪些部分需要进行更深入得探讨。我们需要针对不同的方案,分析各自的利弊,并且解释为什么选择其中的某个方案而不是其他的。
记住不是只有一个唯一的答案,最重要的是在考虑系统约束的同时,权衡不同方案的利弊。

  • 由于我们要存储很大量的数据,应该怎么对数据进行分片从而存进多个数据库?是否应该将所有的用户数据存在同一个数据库里面呢?这可能导致什么问题?
  • 应该怎么处理某些特定的用户数据呢,他们可能发布很多推文或者关注很多其他的用户?
  • 由于用户的时间线将会包括最新的推文或者最相关的推文,是不是应该考虑以某种特定的方式存储用户数据从而有利于获取用户最新推文呢?
  • 应该使用多少缓存来加速呢?在哪一个层次来加缓存呢?
  • 哪个组件需要更好的负载均衡?

第七步:找出并解决瓶颈

尝试找出尽可能多的系统瓶颈,并且考虑多种不同的方案来缓解。

  • 系统是否存在单点问题?做什么可以缓解?
  • 数据是否有副本,以至于如果其中的部分节点损坏仍然能对用户提供服务?
  • 同样的,正在运行的服务是不是有足够多的副本,其中的一部分失败可能导致整个系统挂掉吗?
  • 怎么监控系统的性能?当核心组件失败或者性能下降,能收到监控吗?

总结

简单地说,做好准备和面试中的条理性是 SDI(系统设计面试)中的取胜之匙。上述提到的步骤应该指导面试者保持在正轨上,并且覆盖所有的考虑面。
我们一起把上面的方法,用于下面几个面试中常会问道的系统设计上面吧。

本文使用 文章同步助手 同步