Interviewer: Today we are going to have a system design interview. Our goal is to design Instagram. You don't have to cover everything. We can drill into specific topic when we get there. Are you familiar with Instagram?
Interviewee: Ya, I use it all the time. It's photo sharing app. Before we start, can we take a step back? What's the purpose of this “Instagram” we are building? Are we trying to compete with the real thing?
Interviewer: Let's imagine that Instagram doesn't exist yet and people don't have a good app to share photos broadly.
Interviewee: Good to know. What features do we want to cover? I think we have two basic features - upload a photo, get a feed from followers and follow/unfollow . Sounds good?
Interviewer: Cool, that's a good list. Let's focus on the first two for the sake of time.
Interviewee: How about latency? I think this would be an important requirement too. I assume we want to have minimal latency - probably less than 0.5 second for loading feed.
Interviewer: That's a good callout.
Interviewee: OK. (write on whiteboard). So functional requirement is to support 1) uploading photo and 2) retrieve feed from followers. Non-functional requirement is 1) Feed retrieve latency of less than half a second, 2) highly available. 3) highly reliable (data never lost). Non-requirement is follow & unfollow.
Interviewer: Makes sense.
Interviewee: Do we need to consider feed ranking?
Interviewer: Let's skip that for simplicity sake. Let's assume feed is ranked in reverse chronological order. Basically latest photo on top.
Interviewee: I assume there are a lot of people want to use this service. Shall we assume the scale of the service is similar to the real one?
Interviewer: Yes. Let's assume daily active user is 800M.
Interviewee: How often do people upload?
Interviewer: Let's assume people post every 10 days.
Interviewee: Assuming daily active user makes 10 requests a day and post every 10 days. I think we can calculate the read/write QPS. (Write on whiteboard) I think read QPS is 800 * 1000 * 1000 * 10 / (3600 * 24), roughly 90k QPS and write QPS is 1/100th of that which is 900 QPS. Don’t think this will fit on one machine. (Laugh)
Interviewer: No, it won't.
Interviewee: We will need a lot of storage here. Majority would be to store photos. Assuming we store all photos for 5 years and a photo is 1M, we will need 800 * 1000 * 1000 * 365 * 5 * 1M / 10 = 146000 TB = 146 PB. It will take one or multiple data centers to hold.
Interviewer: Sounds good. Let's proceed with a high level design.
我们进一步地对非功能性需求进行量化和细化。
Feed Latency <0.5s
Support 800M DAU
Highly available (while supporting read 90k QPS, write 900 QPS)
Post Table (post id as primary key)- post id,user id,image url,create time
User Table (user id as primary key) - user id, user name, profile photo url, join time
Feed Table (user id as primary key) - user id, post id, create time; 提两点,1) 这里create time是必须的,在返回Feed过程中我们需要按照这个排序。因为我们用了async worker来写feed table, 我们无法保证先写进来的一定就是create time更早的照片。2) 选用user id做primary key优于post id,也可以考虑用 user id 加上create time 来做 composite key.
Follow Table - user id, follower id 或 user id, following id; 这边要说明Trade off - Push方案里我们总是拿user id去找他被谁follow了,而pull方案里我们总是拿user id去找他follow了谁。当然我们也可以两种都存来做一个hybrid approach,下面会提到。
这里值得讨论一个细节,我们能不能定一个Follower数量的限制,这样是不是就不用专门用一张新的post table去存了呢?其实不然,因为用户的Follower数量是会波动的,如果用户正好在那条线上,会造成fanout时有时无的情况。当然,我们建了新的celebrity post table也会带来问题,就是如果有了新人一下子变很火,我们怎么把他们加入这个我们认定的celebrity的行列。方法很简单,其中一种是从普通的post table去backfill celebrity post table,另一边从Feed table里去除他们的post.