• 什么是新鲜事 News Feed?
• 你登陆 Facebook / Twitter / 朋友圈 之后看到的信息流 • 你的所有朋友发的信息的集合
• 有哪些典型的新鲜事系统? • Facebook • Twitter • 朋友圈 • RSS Reader
• 新鲜事系统的核心因素? • 关注与被关注 • 每个人看到的新鲜事都是不同的
优秀的分析文章:
转:微信朋友圈的设计分析
Storage 存储 – Pull(拉) Model
• 算法• 在用户查看News Feed时,获取每个好友的前100条Tweets,合并出前100条News Feed
• K路归并算法 Merge K Sorted Arrays
• 复杂度分析
• News Feed => 假如有N个关注对象,则为N次DB Reads的时间 + N路归并时间(可忽略)
• Post a tweet => 1次DB Write的时间
为什么 N 路归并算法 的耗时可以忽略?
Storage 存储 – Pull 原理图
用户
Interviewer: Pull模型有什么缺陷么?
Storage 存储 – Pull Model
• getNewsFeed(request)
• followings = DB.getFollowings(user=request.user)
• news_feed = empty
• for follow in followings:
• tweets = DB.getTweets(follow.to_user, 100)
• news_feed.merge(tweets)
• sort(news_feed)
• return news_feed[:100] # 返回前100条
N次DB Reads非常慢 且发生在用户获得News Feed的请求过程中
• postTweet(request, tweet)
• DB.insertTweet(request.user, tweet) • return success
Storage 存储 – Push(推) Model
• 算法
• 为每个用户建一个List存储他的News Feed信息
• 用户发一个Tweet之后,将该推文逐个推送到每个用户的News Feed List中
• 关键词:Fanout • 用户需要查看News Feed时,只需要从该News Feed List中读取最新的100条即可
• 复杂度分析
• News Feed => 1次DB Read
• Post a tweet => N个粉丝,需要N次DB Writes • 好处是可以用异步任务在后台执行,无需用户等待
Interviewer: Push模型有缺陷么?
• getNewsFeed(request)
• return DB.getNewsFeed(request.user)
• postTweet(request, tweet_info)
• tweet = DB.insertTweet(request.user, tweet_info)
• AsyncService.fanoutTweet(request.user, tweet) – 异步执行
• return success
• AsyncService::fanoutTweet(user, tweet)
• followers = DB.getFollowers(user)
• for follower in followers:
• DB.insertNewsFeed(tweet, follower) – followers的数 目可能很大
Pull vs Push
pull模式 :客户端主动从服务端拉取消息。
优点:客户端不存在消息堆积的情况。
缺点:消息处理不及时,可能存在大量无效请求,客户端需要考虑拉取频率逻辑
push模式 :服务端主动给客户端推消息的。
优点:消息及时到达。
缺点:无法感知客户端的消费能力,可能造成客户端消息堆积
Storage 存储 – Pull vs Push
• 热门Social App的模型
• Facebook – Pull
• Instagram – Push + Pull
• Twitter – Pull
• 朋友圈 - ?
Push:当Producer 发出的消息到达后,服务端马上将这条消息投递给 Consumer。
Pull:当服务端收到这条消息后什么也不做,只是等着Consumer 主动到自己这里来读,即 Consumer 这里有一个“拉取”的动作(即为你来我就服务)。
• 误区
• 不坚定想法,摇摆不定
• 不能表现出Tradeoff的能力
• 无法解决特定的问题
• 用过前3个步骤的分析,我们已经得到了一个可行方案
• Scenario 场景 和面试官讨论 搞清楚需要设计哪些功能 并分析出所设计的系统大概所需要支持的 Concurrent Users / QPS / Memory / Storage 等
• Service 服务 • 合并需要设计功能,相似的功能整合为一个Service
• Storage 存储 • 对每个 Service 选择合适的存储结构 • 细化数据表单 • 画图展示数据存储和读取的流程
• 得到一个 Work Solution 而不是 Perfect Solution • 这个Work Solution 可以存在很多待解决的缺陷
Scale 扩展 How to Scale?
系统如何优化与维护 1. Optimize 优化 2. Maintenance 维护
Scale 扩展 - 如何优化系统
• 第一步 Step 1: Optimize
• 解决设计缺陷 Solve Problems
• Pull vs Push
• 更多功能设计 More Features • Like, Follow & Unfollow, Ads
• 一些特殊情况 Special Cases • 鹿晗关晓彤搞挂微博, 僵尸粉
• 第二步 Step 2: Maintenance
• 鲁棒性 Robust • 如果有一台服务器/数据库挂了怎么办
• 扩展性 Scalability • 如果有流量暴增,如何扩展
Scale 扩展 – 解决Pull的缺陷
• 最慢的部分发生在用户读请求时(需要耗费用户等待时间)
• 在 DB 访问之前加入Cache • Cache 每个用户的 Timeline
• N次DB请求 → N次Cache请求 (N是你关注的好友个数)
• Trade off: Cache所有的?Cache最近的1000条?
• Cache 每个用户的 News Feed
• 没有Cache News Feed的用户:归并N个用户最近的100条Tweets,然后取出结果的前100条
• 有Cache News Feed的用户༚ 归并N个用户的在某个时间戳之后的所有Tweets
• 课后作业:对比MySQL 和 Memcached 的 QPS • Memcached QPS / MySQL QPS ~ 100 ~ 1000
Scale 扩展 – 解决Push的缺陷
• 浪费更多的存储空间 Disk
• 与Pull模型将News Feed存在内存(Memory)中相比
• Push模型将News Feed存在硬盘(Disk)里完全不是个事儿
• Disk is cheap
• 不活跃用户 Inactive Users
• 粉丝排序 Rank followers by weight (for example, last login time)
• 粉丝数目 followers >> 关注数目 following
• Lady Gaga问题
• 无解?完全切换回Pull?
• Trade off: Pull + Push vs Pull
看到这里的时候,我忍不住亲了布鲁斯一口,他痛快的描述出了我一直以来在工作中说不清道不明的烦躁,因为你总会遇到这样的人,同时很难发现自己到底是不是这样的人。
我在工作前3年其实如履薄冰,感觉自己什么都学了,但去了公司发现什么都不会,怀揣着自我否定一点点完成别人布置的任务,直到工作5年以后才有一种醍醐灌顶的感觉,理解了自己做的是什么,接下来要学习哪个方向,以前学到那么多东西究竟是怎么串联起来的,这是一种打通任督二脉的满足感。
等到工作8年之后,才真正开始回头看Java语言,对以前烦厌欲呕的Java基础提起莫名的兴趣,同时喜欢看书,写案例,尝试阅读别人的源码等等,此时我才真正有自己一只腿迈进Java领域的意识。
同时,在工作中会对许多能力一般但沟通较为偏执的同事产生抵触情绪,我有时会认为这是一种大人看小孩耍脾气的感觉,这个只有在工作多年之后才会产生,作者很准确的阐述出了我描绘不出的这种解释。
同样的,我认为在这个成长的过程中,我一定也成为过别人心中眼高手低的人。
我在这里能分享给大家的经验就是,在工作中多学习少争论,多和厉害的人走近一点,虚心把对方的东西都学过来,长此以往你会进步神速,这不是你在网上学习能得到的,一定是在工作中。