搞定面试中的系统设计题
一般来说,社招多少都会考察系统设计题,尤其是针对有了1年以上工作经验的工程师,这更是面试流程中的家常菜, 除了面试之外,学会系统设计对我们的日常工作也是大有裨益的。
每当我说起“设计”,就会联想到“战略”这个词语,战略的意思就是,选择一方面,而放弃另一方面。而软件设计也是这样, 我们在多个需求、现实资源中进行权衡,从而给出一套符合当前,以及短期未来中最优的方案。下面这张图画出了后端中常见的 一些结构,此外,还可以参考我的 《Web开发简介》
系统设计题想要考察什么?
首先我们要搞清楚,面试官为什么要问这一类题目。这一类题目的特点是题目非常开放,涉及到的知识点特别多,每一个地方都可以进行 深挖,而且,往往一个条件进行改变,相对较好的方案就会完全不同。通常来说,面试官使用系统设计题,主要就是考察:
- 思维是否开阔、灵活
- 知识广度
- 知识深度
- 架构能力是否足够,是否能设计出可扩展的系统
- 是否能熟练使用常见工具并且知晓特性,能够因地制宜选择合适的方案和工具
因此,此时我们已经知道面试官到底想要通过系统设计题考察我们什么,我们也就知道在大的方面,该要怎么应对面试:
- 加强知识深度:知识深度不仅仅在系统设计题中体现,包括此前的技术面试题都会考察
- 加强知识广度:了解常见工具的特性和什么场景适合,例如MySQL/PG、Redis。了解常见的业务场景特点,例如:论坛一般来说都是读多写少,而日志服务则是写多读少。
- 了解常见的软件架构模式,参考 这里
如何解决系统设计题
那么,当我们拿到一个具体的系统设计题时,我们应该怎么着手解决它呢?
注意陷阱⚠️
千万不要张口就来 。我们上面的分析已经表明,遇到问题,我们必须先明确问题,然后才能给出有效的解决方案,系统设计题也是如此。 而往往面试官会在这里面挖坑,就是,给出一个条件相当模糊的系统设计题。例如:请设计一个短链接系统、请设计一个压测工具。
如果我们张口就来,那么就中了面试官的套路。在工作中也是如此,如果你碰到了一个产品经理,产品经理还没有想清楚,就叫你写代码, 那么后面一定是反复修改需求,作为工程师的你一定是痛不欲生。因此,我们需要做的第一步就是 明确问题,不要张口就来。
千万不要自己一个人闷头想,记得一边想一边把你的思路说出来 由于别人是不知道你咋想什么的,如果你一个人皱着眉头思考,面试官 也很着急,他也不知道你是想不出还是怎么的。所以,一边把你的思路描述出来一边想,这是最好的方式。
明确问题
拿到了问题之后,我们只有一个大概的方向,即我们要做一个什么。但是并不清楚具体条件,而不同的的业务场景所需要的架构也是不一样的。 例如,每天只有10000次访问的短链接系统和每秒钟有10000次访问的短链接系统在技术要求上是不一样的。
因此,第一个我们需要问面试官的问题:
- 这个系统一天的请求量是多少?面试官回答,一天2万。
虽然现在我们有了更加明确的条件,但是还是不够具体,我们需要进一步确定:
- 这个短链接系统的峰值是多少?面试官回答:峰值1万/s。
此刻,我们得到的条件已经清晰很多,我们知道,QPS位10k,相对来说,已经算是高并发了,但是我们还需要继续确定。
- 这个短链系统的短链接是永久保存的吗?面试官回答:是。
- 生成的锻炼字符长度有要求吗?越短越好。
- 录入的长链接大概是多少个字符呢?有长有短。
- 那么,平均是多少个字符呢?假设是50个字节。
通过这些我们可以了解到,我们需要对短链接进行持久化。而到目前为止,我们可以得出一些计算:
- 峰值QPS为10k
- 一天的请求量为20k,因此,我们的系统需要为并发考虑,所以我们很可能需要用到缓存
- 一天为20k,那么一年就是
20k * 365 = 7300k
- 如果短链接生成的链接长度为 6个字符,那么一年下来,需要的磁盘大小至少是:
7300k * (6 + 50) / 1024 = 399.21875M
然后我们需要了解一些常见的性能指标,这是我们需要记在脑子里的:
- Nginx:能轻松的处理c100k问题,内存越大,能处理的并发量越高
- Redis: https://redis.io/topics/benchmarks 表明,对于GET/SET来说,QPS 10-100k没啥大问题
- MySQL: https://www.mysql.com/why-mysql/benchmarks/ 表明,对于只读,QPS 几百k没啥问题,对于写,MySQL 5.7 QPS 100k 几乎是上限
- PG: https://www.percona.com/blog/2017/01/06/millions-queries-per-second-postgresql-and-mysql-peaceful-battle-at-modern-demanding-workloads/ 也是差不多
这些数据是不准确的,因为:
- 和怎么用关系很大
- 和硬件配置关系很大
但是我们心里还是要有个大概印象。
现在我们就可以知道了,这个短链系统,并发是10k/s,但是我们还漏掉了一个问题,就是没有明确是读的QPS是10k还是写,还是读+写。 这个时候我们仍然可以向面试官提问。但是,不论读还是写,QPS为10k,只要硬件配置过得去,不加缓存也ok。但是通常来说,我们还是会 选择加上,原因很简单:此刻都有QPS都有10k,保不准半年之后更高了。
到此为止,我们已经知晓了解决系统设计题的一个大概思路流程,但是这远远不够,因为系统设计题对知识的广度要求也很高,因此这里 我还会提供一些常见的知识,希望能够对大家有所帮助。
常见知识
- 最简单粗暴也是实践中最常用的应对方案就是:升级机器、加机器(所以架构的时候要考虑好水平扩展)
- 大多数应用都是读大于写,解决方案很简单:加缓存+读写分离
- 对于写大于读的方案,见 (关系型)数据库优化总结
- Nginx:能轻松的处理c100k问题,内存越大,能处理的并发量越高
- Redis: https://redis.io/topics/benchmarks 表明,对于GET/SET来说,QPS 10-100k没啥大问题
- MySQL: https://www.mysql.com/why-mysql/benchmarks/ 表明,对于只读,QPS 几百k没啥问题,对于写,MySQL 5.7 QPS 100k 几乎是上限
- PG: https://www.percona.com/blog/2017/01/06/millions-queries-per-second-postgresql-and-mysql-peaceful-battle-at-modern-demanding-workloads/ 也是差不多
- B, M, G, T, PB之间的关系换算要清楚
- 需要长时间处理的任务或者是强依赖网络(而网络不确定性大的问题),妥妥的用队列,例如消息推送
- 性能优化套路:加机器 - 加缓存 - 优化数据库索引 - 垂直拆数据库表 - 水平拆数据库表 - 垂直分库 - 水平分库
此外欢迎读者提供更多常见知识我收录到此处。
练习题
- 设计一个短链接系统
- 设计一个压测工具
- 设计一个分布式ID生成器
- 设计一个论坛
- 设计一个微博
- https://github.com/donnemartin/system-design-primer
- https://www.hiredintech.com/system-design
- https://soulmachine.gitbooks.io/system-design/content/cn/
- https://leetcode.com/discuss/interview-question/system-design
更多文章
- socks5 协议详解
- zerotier简明教程
- 搞定面试中的系统设计题
- frp 源码阅读与分析(一):流程和概念
- 用peewee代替SQLAlchemy
- Golang(Go语言)中实现典型的fork调用
- DNSCrypt简明教程
- 一个Gunicorn worker数量引发的血案
- Golang validator使用教程
- Docker组件介绍(一):runc和containerd
- Docker组件介绍(二):shim, docker-init和docker-proxy
- 使用Go语言实现一个异步任务框架
- 协程(coroutine)简介 - 什么是协程?
- SQLAlchemy简明教程
- Go Module 简明教程