Scenario:
MAU -- monthly active users
step1: 罗列功能
-
register/login
-
user profile display/edit
-
upload image/video
-
search
-
post/share a tweet
-
timeline/news feed
-
follow/unfollow a user
step2: 选出核心功能
-
post a tweet
-
timeline某个用户发的所有帖子
-
news feed关注的用户所有帖子
-
follow/unfollow a user
-
register/login
并发用户
-
average concurrent user: 日活跃*每个用户平均请求次数/一天多少秒 = 150M * 60 / 86400 ~100k
-
peak: 峰值 average concurrent user * 3 ~300k
-
快速增长的产品:max peak users in 3 month = peak * 2
读频率 read QPS: 300k
写频率 write QPS: 5k
QPS的影响:
-
100 笔记本做web服务器 (实际情况超过10)
-
1k 好点的web服务器,single point failure
-
1m 1000台web服务器的集群cluster,需要考虑maintainance
QPS和web server/database之间的关系
-
一台web server承受量 1k QPS(考虑到逻辑处理时间以及数据库查询的瓶颈)
-
一台sql db承受量1k QPS(如果join和index query比较多,值会更小)
-
一台nosql db(Cassandra)承受量是10k的QPS
-
一台nosql db(Memcached)承受量是1M的QPS
Service:
router
user service: register, login -> sql(mysql)
tweet service: post a tweet, news feed, timeline ->nosql(mongodb)
media service: upload image, upload video -> file system(s3)
friendship service: follow, unfollow -> sql/nosql
Storage:
select 为每个service选择存储结构,schema细化结构
数据库系统
-
sql database: user table
-
nosql database: tweets, social graph(followers)
文件系统: 图片、视频,media files
缓存系统: 不支持数据持久化,效率高,内存级访问速度
design schema
user table
id primary key
username varchar
email varchar
password varchar
friendship table
from_user_id foreign key
to_user_id foreign key
created_at timestamp
tweet table
id integer
user_id foreign key
content text
created_at timestamp
news feed 如何存取
核心因素: 关注与被关注,每个人看到的新鲜事不同
pull model:
算法:获取每个好友的前100条tweets。merge k sorted arrays
复杂度分析:news feed -> n个对象, n * db reads(file system) + k路归并(内存上执行)
post a tweet -> 1次db write时间
pull原理图
client - 1. give me new feed -> web server <- 2. get followings -> friendship table
<- 4. merge and return---| <- 3. get tweets from followings -> tweet table
缺陷:用户在获得news feed的时候现算, 慢
getNewsFeed(request)
followings = DB.getFollowings(user = request.user)
news_feed = empty
for follow in followings:
tweets = DB.getTweets(follow.to_user, 100) // n次db reads很慢,且发生在用户获得news feed的请求过程中
news_feed.merge(tweet)
sort(news_feed)
return news_feed[:100] // 返回前100条
postTweet(request, tweet)
DB.insertTweet(request.user, tweet)
return success
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
可以用异步人物在后台执行,无需用户等待
news feed table(denormalize存取冗余信息,读取快)
id integer
owener_id foreign key
tweet_id foreign key
created_at timestamp // 排序
20个/页面,得到tweet_id,去查具体内容。把所有tweet放在cache里。
select*from news_feed_table where owner_id=xx order_bu created_at desc limit 20;
post 流程图
client <-1.post a new tweet-> web server <-2. insert the tweet to db-> tweet table
|
3.send tweets to my friends
|
async tasks server <- 4. get followers-> friendship table
- 5.fanout: insert new tweet to followers news feed-> news feed table
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)
follower = DB.getFollowers(user)
for follower in followers:
DB.insertNewsFeed(tweet, follower)
pull vs push
fb - pull, twitter - pull, instagram - push + pull
Scale:
step 1: optimize
solve problems: pull vs push
pull的缺陷:最慢的地方发生在用户读请求(耗费用户等待时间)
- 在db访问之前加入cache
- cache每个用户的timeline
- n次db请求 -> n次cache请求 qps100 - 1000倍差距
- tradeoff: cache每一个用户最近的200条
- cache每个用户的news feed
- 没有cache news feed的用户:归并n个用户最近的100条tweets,然后取出结果的前100条
- 有cache news feed的用户:归并n个用户的在某个时间戳之后的所有tweets
push的缺陷:
- 浪费更多的存储空间disk(disk is cheap)
- 不活跃用户 rank followers by weight(for example, last login time)
- 粉丝数目followers >> 关注数目following trade off: pull + push 尝试在现有的模型下做最小的改动来优化,比如多加几台用于做push任务的机器。长期的增长进行估计。
- 明星用户不push到news feed
- 当用户需要的时候,来明星用户的timeline里取,并合并到news feed里
- 如何定义明星?离线计算 is_superStar boolean
什么时候用push?资源少,双向好友关系,无明星问题,实时性要求不高,用户发帖比较少
什么时候用pull?资源多,实时性要求高,用户发帖很多,单向好友关系,有明星问题
more features: like, follow, unfollow, ads
special cases
step 2: maintenance
robust: 一台服务器/数据库挂了怎么办
scalability:流量暴增如何扩展