专栏名称: 九章算法
专业的北美IT求职经验分享、技术交流社区,帮助你找到好的IT工作。由硅谷顶尖IT企业工程师维护。提供专业的算法培训/面试咨询,官网 www.jiuzhang.com
目录
相关文章推荐
算法爱好者  ·  谷歌创始人每天回公司敲代码,认为员工用 ... ·  4 天前  
九章算法  ·  LeetCode刷题小分队,day2:10月 ... ·  6 天前  
九章算法  ·  不鸽了!Github ... ·  1 周前  
九章算法  ·  Meta E5据说顶包 ·  1 周前  
51好读  ›  专栏  ›  九章算法

东邪老师讲解 Post 系统的设计

九章算法  · 公众号  · 算法  · 2017-01-13 07:59

正文

实现前5分钟,1小时,24小时内分享最多的post的系统

这个题是一个很好的面试题,因为可以从算法和系统两个角度进行考察。


从算法的角度分析



可以简单的称之为 

Top K Frequent Elements in Recent X mins.



算法的角度,本质就是设计一个数据结构,支持给某个key的count+1(有一个post被分享了),给某个key的count-1(有一个分享的计数已经过期了),然后查询Top k。

做法是维护一个有序序列(用链表来维护),每个链表的节点的key是 count,value是list of elements that has this count,也用linked list串起来。 比如 a出现2次,b出现3次,c出现2次,d出现1次。那么这个链表就是:

{3: [b]} --> {2: [a ->c]} --> {1: [d]}

然后另外还需要一个hashmap,key是element,value是这个element在链表上的具体位置。


因为每一次的操作都是 count + 1和 count - 1,那么每次你通过 hashmap 找到对应的element在数据结构中的位置,+1的话,就是往头移动一格,-1的话,就是往尾巴移动一格。总而言之复杂度都是 O(1)。当你需要找 Top K 的时候,也是 O(k)的时间 可以解决的。


这个数据结构来自 LFU 的论文:
http://dhruvbird.com/lfu.pdf
LintCode上有LFU的题, 可以做一下:
www.lintcode.com/problem/lfu-cache


从系统设计的角度分析

所以一般来说,你可能首先需要按照 LFU 的思路答出上述的方法。这个就过了第一关,算法关。但是还没结束,这个题还有第二关,那就是系统设计关。上面的算法从算法的角度没办法更优了,每个分享操作都是O(1)的代价,每个求Top K都是O(k)的代价。已经很棒了。但是系统的角度出发,会存在这样一些问题:


1

如果QPS比较高,比如 1m,这个数据结构因为要加锁才能处理,所以会很慢。

2

分享的数据本身是分布式的,而不是中心化的,也就是说,比如有1000台web服务器,那么这1000台web服务器的是最先获得哪个帖子被分享的数据的,但是这些数据又都分布在这1000台web服务器中,如果用一个中心化的节点来做这个数据结构的服务,那么很显然这个中心节点会成为瓶颈。

3

比如这个系统用在twitter 这样的服务中,根据长尾理论,有80%或者更多的帖子连 Top 20% 都排不进去。而通常来说,从产品的角度,我们可能只需要知道 Top 20 最多是 Top 100 的数据就可以了。整个系统浪费了很多时间去统计那些永远不会成为Top 100的数据。

4

题目的要求是“5分钟,1小时,24小时”,而不是“最近2分零30秒”,“最近31秒”,也存在较大的优化空间

5

真实产品实时性要求和准确性没有那么高。你需要查询最近5分钟的Top K,结果得出的是最近5分02秒的Top K在产品中是没有太大问题的。

6

查询Top k 的次数远低于count + 1和 count -1 的次数。


综上所述我们给出一些针对性优化策略:


01

分布式统计 Distributed:


每隔5~10秒向中心节点汇报数据


也就是说,哪些帖子被分享了多少次这些数据,首先在 web server 中进行一次缓存,也就是说web server的一个进程接收到一个分享的请求之后,比如 tweet_id=100 的tweet被分享了。那么他把这个数据先汇报给web server上跑着的 agent 进程,这个agent进程在机器刚启动的时候,就会一直运行着,他接受在台web server上跑着的若干个web 进程(process) 发过来的 count +1 请求。

这个agent整理好这些数据之后,每隔5~10秒汇报给中心节点。这样子通过5~10s的数据延迟,解决了中心节点访问频率过高的问题。这个设计的思路在业界是非常常用的(做这种数据统计服务的都是这么做的),我们在《系统设计班》的datadog一节的课中,就讲到过用这种思路来统计每一个event发生了多少次。

02

分阶段统计 Level


在《系统设计班》的 ratelimiter 一节课中,我们也提到了这种分阶段统计的思想。

即如果我要去算最近5分钟的数据,我就按照1秒钟为一个bucket的单位,收集最近300个buckets里的数据。如果是统计最近1小时的数据,那么就以1分钟为单位,收集最近60个Buckets的数据,如果是最近1天,那么就以小时为单位,收集最近24小时的数据。那么也就是说,当来了一个某个帖子被分享了1次的数据的时候,这条数据被会分别存放在当前时间(以秒为单位),当前分钟,当前小时的三个buckets里,用于服务之后最近5分钟,最近1小时和最近24小时的数据统计。

你可能会疑惑,为什么要这么做呢?这么做有什么好处呢?

这样做的好处是,比如你统计最近1小时的数据的时候,就可以随着时间的推移,每次增加当前分钟的所有数据的统计,然后扔掉一小时里最早的1分钟里的所有数据。这样子就不用真的一个一个的+1或者-1了,而是整体的 +X 和 -X。当然,这样做之后,前面的算法部分提出来的数据结构就不work了,但是可以结合下面提到的数据抽样的方法,来减小所有候选 key 的数目,然后用普通的 Top K 的算法来解决问题。

参考练习题:http://www.lintcode.com/en/problem/top-k-frequent-words/

03

数据抽样 Sample


可以进行一定程度的抽样,因为那些Top K 的post,一定是被分享了很多很多次的,所以可以进行抽样记录。


如果是5分钟以内的数据,就不抽样,全记录。如果是最近1小时,就可以按照比如 1/100 的概率进行 sample。


这个思想我们在Web Crawler 的那节课中提到过。


04

缓存 Cache


对于最近5分钟的结果,每隔5s才更新一次。
对于最近1小时的结果,每隔1分钟更新一次。
对于最近24小时的结果,每隔10分钟才更新一次。

用户需要看结果的时候,永远看的是 Cache 里的结果。另外用一个进程按照上面的更新频率去逐渐更新Cache。


总结

以上的这些优化方法,基本都基于一个基本原则:在很多的系统设计问题中,不需要做到绝对精确和绝对实时。


特别是这种统计类的问题。如果你刷算法题刷很多,很容易陷入设计一个绝对精确和绝对实时的系统的误区。一般来说面试中不会这么要求,如果这么要求了,那说明考你的是算法,算法才需要绝对准确和实时。


这道题考了算法,大数据系统设计。Top K 的算法我们在九章算法班中讲过,Top K的大数据算法我们在大数据班中讲过,系统设计中用到的各类思想基本也都在课上讲过。所以好好听课很重要

系统设计班,本周末开课,第一节免费试听


系统设计班本周开课,免费试听如何设计twitter!

美西时间 1月15日周日 13:30-15:30

美东时间 1月15日周日 16:30-18:30

北京时间 1月16日周一 05:30-07:30 a.m

报名网址http://t.cn/RAC7Era, 或猛戳“阅读原文”