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:流量暴增如何扩展

posted on 2024-01-08 09:36  dddddcoke  阅读(11)  评论(0编辑  收藏  举报