系统设计面试题学习笔记

通用模板

系统设计的面试题是一个开放式的对话,大体可以分成四个步骤:

第一步:明确需求

  • 需求是无止境的,需要明确一个范畴,专注讨论范畴内的需求用例
  • 目标用户是谁,用户规模大概多大
  • 有哪些功能,核心功能的输入输出是什么
  • 有哪些边界情况需要注意
  • 预期的数据量和处理速度

第二步:设计架构

  • 做一个高层次设计,画出核心组件的架构
  • 用连线代表核心组件之间的交互

第三步:细化实现

  • 具体到每个系统组件,提供具体的解决方案
  • 演示所有功能的交互流程和解决方案

第四步:扩展设计

  • 不用直接得出最终设计,通过 benchmark/load/profile 迭代分析瓶颈
  • 可参考方案:DNS、CDN、负载均衡、水平扩展、反向代理、应用层、缓存、主从复制
  • 论述可能的解决办法和代价,每件事情需要权衡利弊做出取舍

案例一:粘贴板分享平台

第一步:明确需求

我们将问题的范畴限定在如下用例:

  • 用户:输入文本,保存后得到一个随机链接
    • 默认不会过期
    • 设置过期时间
  • 用户:输入一个链接,可以查看分享的内容
  • 服务:用户行为统计与分析,可以使用外部服务(Google Analytics)
  • 服务:自动删除过期的内容
  • 服务:高可用,冗余+自动故障转移

范畴之外的用例:

  • 用户:可以注册、登录、查看历史记录
  • 用户:可以设置可见性、过期时间

状态假设:

  • 访问流量不是均匀分布的
  • 打开一个短链接应该是很快的
  • pastes 只能是文本
  • 页面访问分析数据可以不用实时
  • 一千万的用户量,每个月一千万的 paste 写入量,一亿的 paste 读取量,读写比例在 10:1

估算使用情况:

  • 每个 paste 的大小,每条数据大约 2kb
  • 每个月新的 paste 内容在 20GB
  • 平均 4 paste/s 的写入频率
  • 平均 40 paste/s 的读取频率

第二步:设计架构

可以先实现一个比较通用的架构:Client -> Web Server -> API -> SQL/Store -> Analytics

第三步:细化实现

  • 用一个关系型数据库作为哈希表,用来把生成的 url 映射到一个包含 paste 文件的文件服务器和路径上
  • 为了避免托管一个文件服务器,我们可以用一个托管的对象存储,比如 Amazon 的 S3

用例1:输入文本,保存后得到一个随机链接

  • Client 发送一个创建 paste 的请求到反向代理
  • 反向代理转发请求给写接口服务器
  • 写接口服务器执行如下操作:
    • 生成一个唯一的 url
      • MD5 做哈希,Base62 做编码
      • 检查这个 url 在 SQL 数据库 里面是否是唯一的
      • 如果这个 url 不是唯一的,生成另外一个 url
      • 如果我们支持自定义 url,我们可以使用用户提供的 url(也需要检查是否重复)
    • 把生成的 url 存储到 SQL 数据库 的 pastes 表里面
    • 存储 paste 的内容数据到 对象存储 里面
    • 返回生成的 url

用例2:输入一个链接,可以查看分享的内容

  • Client 发送一个获取 paste 的请求到反向代理
  • 反向代理转发请求给读接口服务器
  • 读接口服务器执行如下操作:
    • 在 SQL 数据库 检查这个生成的 url
    • 如果这个 url 在 SQL 数据库 里面,则从 对象存储 获取这个 paste 的内容
    • 否则,返回一个错误页面给用户

用例3:用户行为统计与分析

非实时分析的功能可以通过 MapReduce 之类的服务来计算点击率之类的数据

第四步:扩展设计

  • 迭代地执行这样的操作:
    • Benchmark/Load 测试
    • Profile 出瓶颈
    • 在评估替代方案和权衡时解决瓶颈
    • 重复前面
  • 重要的是讨论在初始设计中可能遇到的瓶颈,以及如何解决每个瓶颈

上面给出的方案中,主要是优化了以下几个方面:

  • DNS:根据用户所在地址分配到不同的服务器上
  • CDN:内容分发加速,客户端访问速度更快
  • Load Balancer:反向代理层做负载均衡,缓解单个 web server 的访问压力
  • Memory Cache:高频热数据放在内存缓存中,缓解数据库查询压力
  • SQL Write Master-Slave & Read Replicas:主从分离提高数据安全性和可用性,slave 上做查询可以缓解生产服务器访问压力过大的问题。
  • SQL Analytics:分析 SQL 的执行耗时,优化应用层的 SQL 语句性能

附录一:如何实现高可用

方法论上,高可用是通过冗余+自动故障转移来实现的。

整个互联网分层系统架构的高可用,又是通过每一层的冗余+自动故障转移来综合实现的,具体的:

(1)【客户端层】到【反向代理层】的高可用,是通过反向代理层的冗余实现的,常见实践是keepalived + virtual IP自动故障转移

(2)【反向代理层】到【站点层】的高可用,是通过站点层的冗余实现的,常见实践是 nginx 与 web-server 之间的存活性探测与自动故障转移

(3)【站点层】到【服务层】的高可用,是通过服务层的冗余实现的,常见实践是通过 service-connection-pool 来保证自动故障转移

(4)【服务层】到【缓存层】的高可用,是通过缓存数据的冗余实现的,常见实践是缓存客户端双读双写,或者利用缓存集群的主从数据同步与 sentinel 保活与自动故障转移;更多的业务场景,对缓存没有高可用要求,可以使用缓存服务化来对调用方屏蔽底层复杂性

(5)【服务层】到【数据库读】的高可用,是通过读库的冗余实现的,常见实践是通过 db-connection-pool 来保证自动故障转移

(6)【服务层】到【数据库写】的高可用,是通过写库的冗余实现的,常见实践是 keepalived + virtual IP 自动故障转移

附录二:常用的优化方案和原理

比如,在多个 Web 服务器上添加 负载平衡器可以解决哪些问题? CDN 解决哪些问题?Master-Slave Replicas 解决哪些问题? 替代方案是什么?怎么对每一个替代方案进行权衡比较?

  • DNS
  • CDN
  • 负载均衡器
  • 水平扩展
  • 反向代理(web 服务器)
  • 应用层
  • 缓存
  • 关系型数据库管理系统 (RDBMS)
  • SQL write master-slave failover
  • 主从复制
  • 一致性模式
  • 可用性模式

参考资料: