https://wizardforcel.gitbooks.io/gainlo-interview-guide/content/sd1.html

https://github.com/checkcheckzz/system-design-interview 有很多别的公司的博客

百万用户?

数据库多副本读。副本下限的应对措施?发现问题快速扩节点

水平扩容问题。可能需要DHT

另外明星可能有热点问题。需要做特殊处理

CDN cache。不多说。无状态。拉数据。尽可能的扩

估算 BACK-OF-THE-ENVELOPE ESTIMATION

大小

2 ** 10 -> 1KB

2 ** 20 -> 1MB

2 ** 30 -> 1GB

2 ** 40 -> 1TB

2 ** 50 -> 1PB

延迟 2020-ver

L1 cache reference 1ns

branch mispredict 3ns

L2 cache reference 4ns

mutex lock/unlock 17ns

main memory reference 100ns

compress 1k 2000ns - 2us

send 2KB 44ns

SSD 随机读 16us

SSD 顺序读 3us

Data center round trip 500us

硬盘随机读 seek 2ms

硬盘顺序读 800us

SLA

99% 一天15分钟不可用,一年3.65天不可用

99.9% 一天1.5分钟不可用,一年9小时不可用

99.99% 一天8.6秒不可用,一年53分钟不可用

99.999% 一天866毫秒不可用,一年五分钟不可用

一个估算的例子,推特,每个月三亿万活跃用户,50%的用户天天用,平均每个用户两个帖子,10%的帖子包含媒体文件,数据保存五年。估算存储使用

DAU就是150万,qps怎么算 15000W * 2 * 24小时/3600秒 = 3500,最多就7000

推特假设就按照kv来存,key 是64 int id,value可以是140B的文字或者1M的媒体文件

媒体文件那就是15000W * 2 * 10% * 1MB=30TB,每天。五年那就是* 365 * 5 55PB

限流器 rate limiter

客户端没法控制。很难控制api怎么使用,毕竟目的不同

服务端需要考虑这个限流器的规模,是不是全局作用域,以及异常处理

放在服务端可以作为一个中间件来做,放在gateway,也可以做成服务的一个插件

限流算法 这里有个总结非常不错 https://segmentfault.com/a/1190000041548601

  • 令牌桶。不同api/ip/等等分类条件设置不同桶大小。还是很好控制的 主要两个参数,桶大小和token使用速率。这个速率
    • 优点就是实现简单,内存使用不大,也能顶住大量请求,缺点就是这个桶大小和token使用速率怎么调整?更好的适应业务
  • 漏桶,就是FIFO队列,按照固定速率消费。FIFO + batch的感觉。也是两个参数,桶大小和消费速度
    • 优点就是对于后端服务,请求时一直稳定的,也不怎么占用内存。缺点就是参数不好调,以及FIFO导致了请求产生了顺序,有些后来的如果正好遇到队列半满导致排队,可能就影响了延迟
  • 固定窗口计数。把时间分片,每个分片一个计数器。分片内超了指标就拒绝,也就是从根上就做好了稳定速率的效果
    • 优点就是稳定,缺点就是请求暴增的突刺会被完全削峰,失败率上天了,窗口分片的边的请求突刺可能造成请求没完全拦截住,放出去多的请求。
  • 滑动窗口日志,上面的改良。一个窗口,开始时间结尾时间保存好。新的来了就加进来,整个窗口,如果超了就拒绝新的加。加新的同时,旧的过期了(低于窗口的旧的)要淘汰。感觉就是个时间边界控制的FIFO队列 time-bounded fifo queue
    • 时间戳要一直存。占用恐怖
  • 滑动窗口计数。窗口定时移动一下。和固定窗口计数一样的问题。

这里看来看去还是令牌桶顺眼

怎么实现?用数据库夸张了。redis简单的incr expire就可以实现 限流的规则是什么?一个例子

domain: auth descriptors:
- key: auth_type 
- Value: login 
- rate_limit:
    unit: minute 
    requests_per_unit: 5

如果是http,请求太多直接429

分布式限流器怎么做?主要就是竞争问题。上锁明显太慢,redis上如何处理?用zset,zcard判定个数是否超标,时间戳排序就行了。

https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#request-rate-limiter

这种玩法前提是redis快。本来redis也是当cache用,没啥毛病。限流器本身也不是什么重要数据

监控你的rate limiter

这里讨论的是http角度的rate limiter,发散一下?其他角度的ratelimiter?

当然,傻逼客户端别瞎几把用也是也个好建议, 客户端cache,客户端降低调用频率,客户端改善重试策略

一致性HASH设计

hash环。然后节点是range还是slot?要最小化,方便迁移

热点key问题如何处理?让key尽可能均匀

设计kv存储?

这个就不说了。

ID生成器

UUID还是时间相关?时间相关是不是要考虑NTP?

snowflake算法?ticket server?

感觉很多相关资料。这里放个TODO

短网址系统

  • 简单思路 hash,如果长度为 7,包含[A-Z, a-z, 0-9],则可以提供62 ^ 7 ~= 3500 亿个 URL。一般的长度也就5/6,够用了
  • 考虑数据插入效率 -> 数据库基于hash做分片就行了。hash冲突处理(甚至可以不处理)
  • 分发问题

网页爬虫

  • 过滤不想要的
  • 大部分网页是重复的,hash去重
  • 下载可能需要大规模无状态下载小服务
  • 一些对抗措施

这个其实是个糙活

推送通知系统/邮件系统

其实和后端服务没啥区别,几个特别注意的点

  • 推送别太频繁,不然用户可能直接取消订阅/关了,要有个限流
  • 重试机制。单点故障容易导致用户丢失信息。要有整个流程ack,用户真正的消费到
  • 整个流程监控

消息流/推特

感觉和上面的差不太多??

fan-out设计?信息是在加载的时候分发还是一开始就分发。一开始就分发,优点就是直白,缺点就是如果是朋友关系链大的,比如大V,很费时间,比如僵尸粉太多的,这种简直就是浪费CPU 拉取的时候做过滤就简单的多。毕竟很多用户使用时间错开,还有很多僵尸用户,用户拉取的时候再过滤分发,就减轻了很多CPU压力,但是加载会慢,但是省,就避免了前面的大V热key问题

这俩就是料理包和现做菜的区别。料理包,如果大量搞,浪费,但是直接。现做菜,如果客人要多菜多,慢,这个比方我觉得挺直白

数据存储问题。用户的信息要有个postid -content对应。要有个postid-userid对应,用来表达发送消息

cache。很多很多cache,要cache内容,要cache id对应关系,多种kv映射

这里也有类似的介绍 https://wizardforcel.gitbooks.io/gainlo-interview-guide/content/sd8.html

聊天系统

私聊/小群组/在线/多终端/推送通知

如何实现

肯定不是直连的。有个chat server接受服务

  • 拿到消息以及通知的对端
    • 在线就发,不在线就存下来等上线再发
      • 要有个信号通知机制/订阅机制/推送机制 总之是一个玩意儿
    • 问题,如何拿到新的返回的消息?最快收到对方的应答?
      • 轮训?短连接/长连接?websocket?ws更简单一些

消息定义

  • message_id - message_from - message_to - content - create_at 还是比较简单的
  • 如果是群聊,就是这样 channel_id message_id user_id content create_at
  • 然后就是交换message id 消费message id了。没啥说的
    • 问题: 下线后的消息分发处理,这个场景和上面的消息流/推特的场景是相同的,提前准备好还是离线用户上线再准备消息的区别,一个浪费资源一个相应慢,看这个人是不是有大量好友收发消息

实际上,这种用户应该走一遍图分析,大量聊天交互的用户,诈骗/招嫖的/HR招聘都有可能。值得分析一波

其他问题

  • 媒体文件怎么搞?
  • 客户端缓存消息数据
  • 端到端加密?用户数据安全,比如一键删除所有数据
  • 加载时间
  • 错误处理。服务掉线影响客户端,服务发现要稳定,要立即切换
  • 消息重发

还是有挺多细节的

搜索引擎提示补全?trending?

实际上就是一个前缀查匹配最热的搜索结果

要求

  • 最快响应
  • 强相关(前缀查肯定相关,也有可能不前缀查??)
  • 结果排序
  • 高可用
  • 扩容能力,要能扛得住大流量搜索

数据简单估算

一千万DAU,每个人搜十次,也就是一个亿,每次搜20个字节的ascii

简单算一下qps

10 000 000 * 10 * 20 / 24 / 36000 两万四,顶天48000

假设20%的搜索是新的,那也就是说每天增加 10 000 000 * 10 * 20 * 20% = 0.4G

简单说,两个服务,一个数据采集,一个查询

数据采集要存一个倒排,内容-查询次数

查询就搜相关字符串的查询次数最高的

这个问题就变成了根据结果构造trie以及维护/分发trie的问题

问题

  • 数据库不适合构建这种数据模型。这种前缀类型数据用trie保存合适,但是每个子树更新频率?复杂。解决方案可能就是加cache cache一下trie树

周期性收集数据构造trie树,trending也是同样的套路

youtube?netflix?

youtube为例子

简单估计

两亿月活

五亿日播放量

五千万创作者

2019年广告收入15亿万美元

咱们退化一下

5百万日活 DAU

每个用户看五个视频

10%的用户每天传一个视频

每个文件300MB,也就是说每天数据增长150T

考虑CDN成本

美国的CDN,CF,每GB 2美分

看视频,5百万 * 5个 * 0.3G * 0.02 每天15万美元。这么玩不得亏死?

视频上传相关技术?编解码?优化上传?好多技术细节。数据DRM?

错误处理也有很多细节

上传失败?编码失败?

其他推流相关,上面有些重复了

云盘?

简单估算 五千万用户,一千万日活,每人10G,每人每天传两个文件,每个文件500K

总空间500PB,qps也就500

实现简单,就是webserver,提供上传下载借口就行

数据库记录用户信息,给个id,根据id找目录,文件要存hash,方便同步校验冲突

文件做不做压缩?支不支持在线预览?

多客户端修改可以退化成聊天系统的同步问题

上传也可以走视频网站上传那个方案的优化,拆分

实际上存储大概率是S3这种磁盘,问题就退化成S3设计?