猴子也能懂的系统设计套路

81739
145
按照之前instant.1point3acres.cn里说到过的,和大家分享一下我对于senior到staff这个档次的系统设计的看法。这个方面其实每个人都会有自己的总结,我的方法论可能也只是适用于我所见到过的公司,大家看一看作为一个参考就好。

TLDR:用Functional Requirement - Nonfunctional Requirement - Data Model / API - Diagram - Failure这个流程,加上充分的准备和知识广度,去应对系统设计面试。

背景介绍
从刚毕业伊始16年的instant.1point3acres.cn,到20年底跳槽1point3acres.com,直到这次随心所欲加面design(我还能打10个!),甚至可以说沉迷系统设计面试。我对于系统设计面试的了解也从术语层面(当年根本都不知道kv数据库是啥,就推荐Riak,脸不脸红呐),最终到达了“当我们面试系统设计时,我们在面试什么”的程度。

因为我偏爱做产品,加上本身总在大公司的产品组(支付,广告等等),本身工作内容里是不会需要做分布式系统设计的,对于很多系统设计面试的考点其实都得靠自己去学习和归纳。同时自己之前几年级别不高,所以并不能作为系统面试的面试官去实际探索系统设计面试。不过好在我可能因为刚毕业就被系统设计面试打击过,就一直或多或少抱有了对于系统设计领域的好奇心(或者说是不服输)。看到一些复杂的现存系统,就会想稍微搞明白到底架构是怎么做的,有机会的话,还可以接触到公司内那些知道为什么要这么做的人去学习一下。

下面的分享,可能对于常年做infra的小伙伴们来说并没有太多的参考价值,主要是对于那些没有太多相关系统经验,或者说做infra面产品,做产品面infra的时候,需要在自己不擅长的系统领域做一些设计的小伙伴,希望能有提供一些思路,来帮助各位准备最适合自己的系统设计套路。

善事利器
一个顺手的白板app
  • 能够面试时随时分享一个链接给面试官。我个人用的是miro,不过有最多3个可编辑白板的限制。
  • 按照个人喜好选择,比如有人就喜欢用工具自带的图形,而不是手绘,那google drawing也可以。
  • 面试前至少用白板app练习过所有常用功能,有些白板app自带的额外功能很不错用。
  • 对苹果笔有手势适配(比如双击苹果笔侧边可以从任意模式切到画笔模式,再双击自动切到橡皮擦,然后就只在两者之间切换)更好。
  • 和recruiter事先确认过可以用这个白板app,绝大部分都是ok的。只要面试官那边能够导出你最后的图,或者截图,就应该没问题。

一套顺手的白板工具
  • 经典的就是ipad pro (11寸就够,不需要13寸)+ 苹果笔(2代最好)。surface套装也可以,其他平板套装也可以,看个人喜好。
  • 屏幕刷新率太低会写起来很不顺手,120hz会很舒适,所以上面推荐的是pro。预算紧张可以忽略这一点。
  • 如果是临时买的,也多花点心思练习一下在上面涂写,适应起来需要不少的时间。可以考虑追加买橡胶笔套,纸质保护膜这些进一步提升书写体验。

一套稳定的VC配置
  • 当你感觉你的面试官的声音断断续续的时候,可能他感觉到的是一样的问题。事先和朋友用对应面试的聊天工具测试下声音稳定程度。
  • 因为你在低头涂写的时候,声音比画面重要,所以可以的话弄一个比默认笔记本内置麦克风更好的麦克风吧。个人用的是blue那个经典的麦克风,但是哪怕是苹果小白耳机(有线的那个)的音质都是可以的。
  • 你永远不知道你提到的某个你自以为很有价值的点,是不是因为音频问题被面试官忽略了,所以这一点投入还是有必要的,外带时不时和面试官做个ack检查。
  • 至于什么三点光源,DSLR摄像头,绿布背景板这些提升画面质量的,个人觉得肯定有比没有好,但是就超出系统设计面试的范畴了。

学习材料
  • 神书DDIA
不用多说,大家都在推荐,我也依然很推荐。但是即便是我个人,我大概目前阅读进度也只有60%不到(而且是多遍),集中在1/2/3/5/6/7/8/9/11这几章,其中9和11没有深入看进去。和隔壁一些小伙伴说的一样,书里很多的理论证明,还有各种细节上的讨论,比如phantom read的具体情况,又或者说serializability vs linearizability,其实对于准备系统面试来说,并不是非常有帮助。更多的反而可能是写在各章summary里的,那些对于名词和术语的总结,还有对于为什么某些东西在系统设计里很重要的简单分析。搭配这些,和一些网上的中文或者英文的读书笔记,就能让你对这些东西有一个比较合适的理解,所以在反复阅读的时候,我个人会先看summary,再回头去看有哪些知识点我了解不足。

但是这本书本身,说到底也只是个入门,在时间允许的情况,其实每一章里的小话题,都能扩展出来无穷无尽的细节。我本身会因为好奇,继续找网上的相关讨论资料,而且我还是偏好于比较好的中文资料,比如juejin.cn这篇博文,直接把DDIA里面关于分布式事务的部分做了非常好的总结,搭配一下,我对DDIA的7/8/9这三章理解直接就飞跃了。而我个人觉得这三章是对于没有做过相关分布式数据读写的小伙伴来说,最晦涩的三章。类似的其他博文,相信各位如果用自己不明白的关键字去中文或者英文网络搜索,也能找到很不错的。1point3acres.com可以作为一个引子。

除了读书笔记外,当然也有很多视频类的对DDIA的总结资料,比如youtube.com。我个人比较喜欢自己搜一堆资料交叉阅读来理解,但是应该也会有不少人比较偏好视频总结的模式。此处我想提到一个额外的视频资料,就是作者本人youtube.com,里面有上传他去年在剑桥大学的一个分布式系统课程(前8个课时是另外一个老师讲的concurrency),有人搬运+机翻到了bilibili.com。内容方面并不对应DDIA,但是也浓缩了很多书里的知识点非常值得阅读。

最终的目的,并不是说吃透这本书里的各种知识点然后对名词和解释倒背如流,而是能够通过和其他学习资料结合起来,在脑子里有个完整的思维导图。可以闭上眼时,对于刚看完的某个系统设计的架构图,对每一个部分的trade-off,可以脑内注解一样去对应到DDIA书上的知识点,然后给出自己的复述(去解释给别人听)。相比你能延绵不绝地抛出wal,lsm,htap,saga,tcc,2pc,2pl,zero copy,consistent hashing这些名词加上简单的解释,面试官可能只是想要你谈availability和transaction,以及为什么需要这个。如果你实际工作里就做到过这么深入的的话,务必请深度讨论,不过首先还是从基础需求开始讨论。



这两个,在我看来就属于,当你一边读DDIA,一边不清楚这里面的很多术语到底是怎么套用到实际的系统设计里的时候,可以作为辅助的材料。两者都是在尽可能广度优先的前提下,覆盖了许多系统设计面试里的考点和问题,然后提供了大量延展阅读,比如一些著名技术系统的paper,,相关产品的公司engineering blog,亦或者是其他人提供的总结。

但是这两个终究也只是为了覆盖尽可能多的考点而存在的,下面我会提到,面试官一般心里会有几个点希望你踩到,但是你如果只是一股脑去踩遍自己能想到的所有点,反而会浪费宝贵的时间。两者我觉得有用到一个就好,因为阅读这两个的过程里,重点不是记住了材料里面写的东西,而是你能通过它给的一个案例,去做自己的搜索,遍历到关联的学术paper,tech talk,fireside chat,技术文章(比如infoq的网站和ytb频道,highscalability的网站以及youtube上各种散落的tech talk,国内的掘金和开发者头条还有各种技术分享平台等等),还有各大公司的engineering blog。在那基础上,能够通过阅读这些延展材料,强化自己对于案例的理解,内化为自己理解过的对应案例的解决方案,万万不可照本宣科去硬记它的解决方案。


educative.io我放在最后,并且单独说,也是觉得应该挺多争议的。我个人认为,对于初次接触系统设计的人而言,这个课是有一定价值的,它算是比上面的资料更加用心帮你做了一个整理+入门,而且它最经典的tinyurl那几节的分析,能让你在短时间内理解到,系统设计时到底是在设计什么。对于不同的常见例题,可能需要踩的点会有哪些。

但是我们来到senior-staff这个级别的系统设计面试的话,这些就够了吗?比起来说不够,我甚至觉得它里面的一些有点复制粘贴意思的trade-off分析(多个不同案例的trade-off分析几乎一样),和它不分场景都上来先算capacity的做法,其实容易把人带进沟里。我这次面试前后加起来快20场系统面试,真的聊到grok里那样需要考虑每一条数据具体有多少,一个也没有(有一个问到了大概是GB还是TB)。这个我后面也会聊到,capacity是有意义的,但是它的意义并不是说你具体算出来要存120TB,你知道是个TB级就足够了。其次就是后面追加的新课之间的复制粘贴感严重,动不动就上NoSQL等等,很明显就是原作者没有这方面的深度思考或者经验,基于之前的内容在写,并没有对于新课主题系统的针对性分析。

因为一样的原因,youtube或者一些独立站上有不少看起来几十万阅读量的up主上传了很多系统设计的视频,也是深度不足且细节问题一堆,都是没有太多实战经验的人,基于一个类似的模版反复套用在不同系统上进行分析,最多会追加一两个针对该系统的特色的设计。作为踩点来说有一点价值,但是本身真的那样去面试是凶多吉少的。这方面我是基本都能找到对应系统的infoq演讲,或者国内很多技术专家分享的类似系统的架构文章,所以不太需要依赖非专家的猜想式设计。

事前准备
  • 练手面试
这个未必要是系统设计面试,因为下面我会单独说。这个可以是你找朋友帮你mock,也可以是各种提供mock服务的公司,也可以是真的拿一些不想去的公司放在前面几天来面试。主要的意义是让你能够习惯表达自己的临时想法,相比coding这种你只要表达好代码思路,和behavior这种你能提前准备好相关回答的面试,系统设计需要你能随时面对一个没遇到过的开放式问题,一边思考,一边又能带着面试官一起深入。这种把自己脑子里想的,有趣的无趣的相关的跑题的想法,在面试压力之下,能够以合适甚至风趣的方式表达出来给面试官的能力,纯手熟尔,无他。方法论层面,当然有各种书和视频,但是其实就和演讲一样,你学会了所有方法,剩下的就是255场练习了。

  • 练手系统设计面试
我单独拿这个出来说,是因为哪怕你学习到位,方法论总结到位,但是如果你要直接上阵,你还是不知道那套方法论在实践的时候会变成什么样。design和implementation之间的差别,我们可太熟悉了。

我此处就要推荐一下interviewing.io这个可以提供mock系统设计面试的网站了,也感谢instant.1point3acres.cn提到了它。价格绝对不菲,不考虑团购价(4次及以上可以8折),指定大厂面试官要225,随机大厂面试官150。但是如果你真的想提升自己的技巧,至少应该去book一次。我运气很好,随机book到了一个netflix的人,而且他的确做了不少系统设计的工作。在当时我已经做了很多准备,也提前练习过自己的一套流程的情况下,面试官一边肯定了我这个套路非常好(perfecto,perfecto),一边也给出了一些feedback,比如面对未知领域的系统设计,面试官的hint应该主动挖掘原因(交流加分),以及scale的讨论(实际上我的流程里的确安排了scale的部分,但是实战和预演就是会差别很大,所以要练习)。

  • 练手乱涂乱画
把你要用的白板和vc工具提前自己习惯好,保证你到时候的精力都能专注在表达上,然后你能以最舒适的姿势去操作你的白板app。通过一定程度的练习,你的字迹和图案,不至于过于潦草让人生草,不对,让人无法理解。然后你可以总结一些自己习惯用的画图方式(比如数据库用圆柱体,虚线代表异步请求,等等),以及白板上哪里放文字分析,哪里放架构图,预留多少空间给可能的额外要求等等。不要随着面试深入,你要不停地擦擦改改,或者一小块区域塞越来越多的信息。

猴子也能懂的套路
此处允许我再次感谢一下instant.1point3acres.cn,因为我的套路法和他的可以说非常相似,也是在看到他的帖子之后,加上其他帖子和一些英文的分享,我恍然大悟出来的东西。和他一样,一旦理解清楚,到目前为止的所有的积累就能活用起来,相比之下,20年年底的知识积累不会比现在差不多,但是没有一个清晰的思路,就是只能想到什么说什么,或者硬套见过的解决方案(这个是大忌)。

首先第一个要理解的,就是为什么套路会如此有效,也就是我个人理解的,当我们系统设计面试时,我们到底在面试什么 :系统设计考察的,严格来说并不是你对某些系统的深入了解,此处比如说一个地图系统,你马上把hilbert curve的算法给他推演出来,或者一个youtube系统,你直接和他聊实时transcoding,面试官一来不是那么感兴趣,二来可能比你更懂这里面的东西,反而能暴露你自己的临时抱佛脚。一个有效的系统面试设计官,考察的是,就是你真的要做一个未知新系统时,你所表现出来的高级工程师的思考过程。诚然,如果你已经做过类似系统,那么很多系统细节你能信手拈来,但是面试官也总有办法考察到你没有负责的部分,然后继续考察你的思考过程。

你可以想想看,当你面对一个新系统的时候,你首先考虑了什么要素,你要写design doc的时候,你在写什么,你要和提需求的bd或者pm开会时,你会带着什么样的问题去找他们,当你需要拉人做这个系统的时候,你是怎么把需求变成分工的。你在工作里肯定做过这样的事,虽然未必目标是一个分布式事务系统,但是思考的过程其实不会差别特别大,在那之中蕴含的,就是(千层)套路,就是一个经验丰富的工程师面对不同类型的需求,能够把它翻译成代码猴子也能懂的工程目标的能力。

以下我会按照我目前偏好的Functional Requirement - Nonfunctional Requirement - Data Model / API - Diagram - Failure这个套路来剖析。

Functional Requirement
搞清楚核心需求,主要的提问时间。这部分争取在5分钟以内解决
  • 系统的使用者是谁,是用户,还是其他服务,还是其他系统。
  • 如何被使用,是有UI界面,还是定期从哪里拿请求,是提供一个接口。
  • 心的容灾需求是什么样,最不希望发生的错误是什么样的。
  • 追问面试官自己整理的需求是否是核心的,避免错过一些小而复杂的功能需求。

Non-functional requirement
次要的提问时间。系统需求之外,有哪些额外的需求需要满足?按照你对系统的理解去offer自己心里觉得最关键的几个点,这部分也是5分钟以内,别展开讨论解决方案,就只是收集信息。
  • Consistency - 是否会有数据不一致导致的问题?是否能接受部分数据copy不一致?电商类肯定就要strong consistency,like button能做到read your own write就可以了,后台型的服务eventual足矣。
  • Availability - 这个系统要有多可用,1年能承受多少down time?3个9是1年8.8小时(1天88秒),4个9是1年53分钟(1天8秒),5个9是1年5.3分钟(1天1秒不到)。
  • Reliability - 是否可以接受一定的错误率+可以重试?数据的durability也可以这里谈下。
  • Performance/Throughput - 整体有多少用户量,大概率多少活跃用户,此处的用户可以是其他系统。QPS大概有多少,peak factor是多少。
  • Read/Write ratio - 读为主还是写为主,用户使用pattern是否是非对称的(比如微博),读写是否有seasonal/recency pattern(比如读的都是最近写的),这个会让后面数据库选型讨论很方便。
  • Scalability - 这个和上面的吞吐的区别是,可以展开问面试官希望这个系统有多scalable,因为这个概念本身其实是有一定模糊性的。单机百万websocket和集群数万分布式事务都可以说scalable。
  • Misc - 这个就要看具体系统,比如金融交易这些,idempotency就很重要,要提到。比如用户facing的系统,latency就最好控制在200ms里(先问latency,别自己主动offer,如果给了一个很大的值,就说明有很大的空间)。
  • 等等 - 这些就需要自己通过上面的学习来总结,我能想到的上面这些肯定还只是其中一部分。

Data Model
上面两个提问之后,就是你开始主导了。此处建议先问面试官偏好,说清楚自己接下来要聊data和api然后画图,data可以和API的顺序进行调转。这部分可能要10分钟左右,但是也不要花太多时间。

基于上面的信息,你心里应该大概知道这个系统里核心的几个data model (面向对象编程101)是哪些方面。然后它们之间的互动,简单粗暴可以用一个junction table去存互动的关系(就像一个edge,比如a like b),复杂点的就是transaction(a pay b)。把这些data model对应的“表”写出来,此处你不用强调它是个sql表还是nosql的结构,就只说我们这个data model里有哪些信息。根据你的理解去填基本的那些信息,比如某一种对象的id,对应的基本信息,根据上面的需求产生的额外信息(比如餐馆系统,用户表里可以放会员状态,打个比方)。基本data model完了,再聊下他们的关系表,此处依然不要说关系表是sql还是nosql,就说某个地方我们放着这个关系。但是如果是比较经典的事务型关系,比如交易,比如订餐,比如发货,并且你了解为什么很多时候用NoSql类系统存这个信息(写为主,读会带着primary key,immutable records,量大,等等),那么就可以主动提一下这一类数据适合放在比如说cassandra里(因为我自己工作里会用到cassandra)。

数据分类的过程中,不用追求完美,哪怕你提前看到过类似系统的设计方案,也不要上来就把n个field都给定义了,或者说你定义某个field要能对应到之前的需求。比如餐馆系统,每2个小时的一张桌子可以算一条记录,但是桌子本身应该有人数或者类型的信息,这个会在谈需求的时候谈到。表与表之间产生联系(比如foreign key那种)一定要说,而且说到这个联系就可以自然地说indexing和sharding,或者说data locality你要怎么做,然后延展到已经定义的field里有哪些可以用来做composite key,secondary index,sorting key还有sharding key等等。同样的,别过于深入,这一步重点不是系统的scalability,而是把data model定义清楚,为了后面服务。

API
这一部分根据面试官偏好,可能非常快,2分钟就过,也可能有些人喜欢和你深入探讨一个好的API设计。但是如果数据部分已经理得很清晰,我是觉得这部分一般最多也就5分钟就能讨论完。

首先都是自己主导,按照需求写出基本的几个API设计,输入是什么,输出是什么,他们是怎么被call到的(同步,异步,批量),以及会返回那几类的结果(成功/失败/有去重效果的成功或者失败/等等)。然后同个API可以根据请求格式的不同有不同的行为。这方面一般来说不会谈太复杂,因为时间关系不可能都涉及到,但是面试官会希望能看到你把已经谈清楚的需求,清晰地反映到API层面,而不是漏掉了一些已经明确的需求(比如job scheduler里能改变优先级这种需求)。

此处一个潜在考点就是你对于API设计的经验积累,能够让一套API围绕一个资源或者目的来服务,而不是说你想到一个API就列举一个API。cloud.google.com可以帮助你理解一些里面的关键要素。REST API和RPC API的基础知识可能会被考到,所以也最好了解清楚。

Diagram
好了,时间过半了,我们终于来到了画图部分。其实在这之前的很多讨论,基本也都已经写在了白板上。此处要重提一下就是,如果1小时面试,上面的4部分消耗20分钟是没问题的,如果是45分钟,那么就必须要压缩,比如functional和nonfunctional放在一起快速过,API部分只提关键核心的部分等等,争取在15分钟这个时间点进入画图部分。

和其他帖子里说的略微不同,我个人觉得,hotspot也好,是否要sharding也好,系统瓶颈分析也好,这些都是只有把图画出来了,明白请求都是怎么hit到哪里的前提下,才好开始分析的。毕竟有时候,你套个cache,或者挂一层LB,实战里就已经很足够,甚至都不用sharding那些。

然后很多同学在这一步可能就会遇到一个很常见的矛盾点 - 假设我知道某个系统大概是这么做的,是否要直接画成那样。我的观点是根据你的理解深度来判断:你是否能够defend自己这个架构?

比如说大家都知道推特类的系统要考虑pull和push,但是其实如果你深入看,类似的系统有做纯pull的,有做纯push的,也有做hybrid的,并没有存在正确答案。push虽然有write amplifcation但是他的实现其实要简单很多,反过来说pull也有一样的看起来美好但是很麻烦的点(不只是单纯数据库压力)。如果你碰巧只看过其中一类的架构图,就原样招呼上去,那么面试官问起来你为什么不用其他的方案,就会非常尴尬。但是如果你的理解广度+深度能到比如zhuanlan.zhihu.com这样,那么你就可以随意把自己知道的正确答案往上招乎了。


Failure
大部分情况下,面试应该就会在上面的部分消耗完时间,但是如果你特别娴熟,飞快地把面试官的各种问题都解决了的话,那么可能他就会问你,是否还有什么别的想谈的。此处其实是一个非常好的信号,说明你已经cover到了他所有的考点,接下来就是纯加分题了。我一般在这个地方,就会开始谈整个系统可能的问题点,然后在一些极端情况下,会产生什么样的问题。也有其他同学觉得可以聊聊logging,monitoring,我本身也可以从系统故障的角度求聊这些。但是你要是有不同的偏好,我觉得是完全okay的,这个也可以算作是Other

此时就是考验你之前知识积累的广度的时候了(抄答案的时候到了),你如果看过类似的系统的分析和博客,就能想到一些很不错而且不容易考虑到的点(比如对数据做了冷存储的情况下,突然大量访问老数据,又比如某个api突然被ddos导致系统故障)。此处就不太需要担心被人考太深入,不像上面的pull push那样如果准备不足贸然说一个方案反而会被问出问题来。打地鼠一样,多打一个是一个。但是如果你到此刻才发现有些自己应该cover的关键点之前居然没谈到,那就走正式流程把这个点在最后几分钟里好好说清楚,不用慌张,这个时间本来就是作为redundancy来给你的。

写在最后
不论如何准备套路,和其他帖子写的一样,你都会碰到不按套路出牌的,和不吃你这套的。在实行套路的时候,也可能随时被要求谈不同的部分,这些都是没办法提前准备好,只能在大量的面试和练习之下,自然而然养成应对的本能。然后每一次面试结束,做好总结归纳,

系统设计面试,说到最后还是面试,还是让对方在短时间内建立起你对的良好印象的一步。能够应对面试官的需求,改变你的套路,也是交流沟通的重要一环。


希望能对苦于系统设计的各位,提供一些帮助。也欢迎各位随时提问,知无不言,言无不尽。
  • 3155
145条回复