如何處理一個系統面試題---基本套路:
4S分析法:Scenario場景、Service、Storage、Scale:
- 程序 = 算法 + 數據結構
- 系統 = 服務 + 數據存儲
核心思想:trade-off分析,用這種設計有什么不好,有什么好
三大步驟
- 向面試官提問:分析功能/需求/QPS/存儲容量
- 畫圖:根據分析結果設計可行方案:Service + Storage
- 優化:研究可能遇到的問題,怎么樣scale
- 過程
-
Scenario 場景
-
和面試官討論
-
搞清楚需要設計哪些功能
-
並分析出所設計的系統大概所需要支持的 Concurrent Users / QPS / Memory / Storage 等
-
-
Service 服務
- 合並需要設計功能,相似的功能整合為一個Service
-
Storage 存儲
-
對每個 Service 選擇合適的存儲結構
-
細化數據表單
-
畫圖展示數據存儲和讀取的流程
-
-
得到一個 Work Solution 而不是 Perfect Solution
-
這個Work Solution 可以存在很多待解決的缺陷
-
1.Scenario : 描述使用場景,約束和假設
- 問清楚自己要做哪些功能
- 問清楚或者說清楚自己要handle多大用戶量,面試官起碼得給你確認這么幾個信息,否則聊不下去。
- 一個是你平均每天handle多少用戶
- 一個是你峰值(最多?不太精確但是形容一下)每天handle多少用戶
- 自己把自己要算的東西都算出來:
- QPS 、存儲size,不非得一口氣全部算完,但是記住最基本的用戶量,然后再說然后的。
分析出QPS的作用:
• QPS = 100
• 用你的筆記本做 Web 服務器就好了
• QPS = 1k
• 用一台好點的 Web 服務器就差不多了
• 需要考慮 Single Point Failure
• QPS = 1m
• 需要建設一個1000台 Web 服務器的集群
• 需要考慮如何 Maintainance(某一台掛了怎么辦)
• QPS和 Web Server (服務器) / Database (數據庫) 之間的關系
• 一台 Web Server 約承受量是 1k 的 QPS (考慮到邏輯處理時間以及數據庫查詢的瓶頸)
• 一台 SQL Database 約承受量是 1k 的 QPS(如果 JOIN 和 INDEX query比較多的話,這個值會更小)
• 一台 NoSQL Database (Cassandra) 約承受量是 10k 的 QPS
• 一台 NoSQL Database (Memcached) 約承受量是 1M 的 QPS
2.創建設計概要
使用所有重要的組件來描繪出一個概要設計。搭架子,我的系統要干嘛,為了做這件事情,我們需要什么組件,怎么安排。這里一切最簡單,保證這個東西可以work,不要有明顯的優化還不做。
這里可以說出要設計哪些服務Service,比如設計一個音樂播放系統,則需要這幾個服務:
- User Service
- Channel Service
- Music Service
3.設計核心組件:Service、Storage服務和存儲
- 將大系統划分為小服務
- 為每個服務選擇合適的存儲結構,以及設計數據庫的表結構
4.Scale度量你的設計
確認和處理瓶頸以及一些限制。舉例來說就是你需要下面的這些來完成拓展性的議題嗎?
- 負載均衡
- 水平擴展
- 緩存
- 數據庫分片
題目1: 設計一個用戶系統,實現功能包括注冊、登陸、用戶信息查詢、好友關系存儲:
-
詢問需求、場景:估算QPS
-
設計可行解:有哪些Service 和Storage,數據流流向是怎樣?
-
(1) AuthService : 負責登錄注冊
-
用戶是如何實現登陸和保持登陸的?
會話表Session,將session_key作為cookie返回給瀏覽器 => 瀏覽器保存cookie => 之后用戶每次向服務器發送訪問,都會自動帶上cookie => 此時服務器會檢測到請求中的cookie中的session_key,得到這個用戶信息.
數據庫中存有一個Session_Table -
Cache-Aside和Cache-Through:
- Memcached 是一個 Cache-aside 型的 Database
服務器分別與 DB 和 Cache 進行溝通 DB 和 Cache之間不直接溝通 業界典型代表:Memcached + MySQL
- Redis 是讀寫操作都很快的 Cache-through 型 Database
服務器只和 Cache 溝通 Cache 負責 DB 去溝通,把數據持久化 業界典型代表:Redis(可以理解為 Redis 里包含了一個 Cache 和一個 DB)
-
-
(2) UserService : 負責用戶信息存儲與查詢
-
(3) FriendshipService: 負責好友關系存儲
- 怎么存?
SQL
vsNOSQL
SQL一條數據以row為單位;
NOSQL的column是動態的,可以無限大,可以隨意添加;一條數據以grid
為單位,row_key + column_key + value = 一條數據
- 以Cassandra為例剖析典型的NoSQL數據結構
- 怎么存?
-
-
優化
- how to scale?
- 單點失效:數據庫sharding和replication
- sharding:
- 縱向切分:直接不同的表存在不同的機器上
- 橫向切分:粗暴的方法直接按照user_id % 10 進行拆分;好的方法:一致性Hash算法
- 單點失效:數據庫sharding和replication
- how to scale?
題目2: 設計一個TinyURL
- 詢問需求、場景:估算QPS (一天有24 * 60 * 60 = 86400秒)
- 詢問面試官日活躍用戶,假設100m
- 推算寫一條TinyRUL的QPS:
- 假設每個用戶每天發0.1條帶tinyUrl的微博,則100M * 0.1 / 86400 ~ 10M / 100k ~ 約等於100
- 峰值Peak QPS: 100 * 2 ~ 200 (假設峰值大概是平時的2倍)
- 推算讀一條TinyURL的QPS:
- 假設每個用戶每天點擊1條帶tinyUrl的微博,則100M * 1 / 86400 ~ 1k
- 峰值Peak QPS: 2k (假設峰值大概是平時的2倍)
- 推算每天產生的TinyURL所占的存儲:
- 100M * 0.1 ~ 10M條,每條url長度100算,一共1G
可以看出來這題流量並不是很大,所以不需要考慮很多的高性能,只要設計出可行解,主要是說明出Service,是怎么存的,怎么取的.
-
設計可行解:有哪些Service 和Storage,數據流流向是怎樣?
-
(1) UrlService:
- UrlService.encode(long_url)
- UrlService.decode(short_url)
- 訪問端口設計:首先將長網址encode為短網址,然后用戶輸入這個短網址,瀏覽器會重定向才到decode后的真正的url
-
(2) Storage數據存取:
- SQL vs NoSQL
• 是否需要支持 Transaction?——不需要。NoSQL +1
• 是否需要豐富的 SQL Query?——不需要。NoSQL +1
• 是否想偷懶?——Tiny URL 需要寫的代碼並不復雜。NoSQL+1
• 對QPS的要求有多高?—— 經計算,2k QPS並不高,而且2k讀可以用Cache,寫很少。SQL +1
• 對Scalability的要求有多高?—— 存儲和QPS要求都不高,單機都可以搞定。SQL+1
• 是否需要Sequential ID?—— 取決於你的算法是什么 -
(3) long2short hash算法:
- 算法1: 進制轉換:Base62,根據數據庫的自增id來, 將這個整數id轉化成6位的62進制的數(0-9, a-z, A-Z)
- 算法2: 隨機生成: 隨機生成一個6位的shortURL,在數據庫查一遍,如果沒用過,則使用;
-
-
優化:
(1)緩存memchache:
(2) sharding -
知識點:
一致性Hash算法
-
在橫向擴展時,減少數據的移動
-
原理:
- 一個簡單的一致性Hash算法:
• 將 key 模一個很大的數,比如 360
• 將 360 分配給 n 台機器,每個機器負責一段區間
• 區間分配信息記錄為一張表存在 Web Server 上
• 新加一台機器的時候,在表中選擇一個位置插入,勻走相鄰兩台機器的一部分數據- Consistent Hashing —— 更實用的方法 :
思想:就是將機器看成1000個虛擬節點,利用key的hash值找key在的機器
具體算法:• 將整個 Hash 區間看做環
• 這個環的大小從 0~359 變為 0~2 64 -1
• 將機器和數據都看做環上的點
• 引入 Micro shards / Virtual nodes 的概念 \-
• 一台實體機器對應 1000 個虛擬點 Micro shards / Virtual nodes
• 每個 virtual node 對應 Hash 環上的一個點
• 每新加入一台機器,就在環上隨機撒 1000 個點作為 virtual nodes
• 需要計算某個 key 所在服務器時 \-
• 計算該key的hash值——得到0~2 64 -1的一個數,對應環上一個點
• 順時針找到第一個virtual node
• 該virtual node 所在機器就是該key所在的數據庫服務器 \
• 新加入一台機器做數據遷移時 \
-
• 1000 個 virtual nodes 各自向順時針的一個 virtual node 要數據
-
數據庫備份 -- Replica 數據備份
-
Replica
• 是實時的, 在數據寫入的時候,就會以復制品的形式存為多份
• 當數據丟失的時候,可以馬上通過其他的復制品恢復
• Replica 用作在線的數據服務,分攤讀 -
MySQL Replica
Master-Slave模式:Master 負責寫,Slave負責讀
-
題目3: 設計一個推特
-
詢問需求、場景:
-
(1) 估算QPS
- 日活用戶:150M,每個用戶每天平均請求次數:60,則QPS = 150M * 60 / 86400 ~ 100k
- 峰值Peak = 100k * 3 ~ 300k
- 讀頻率:300k
- 寫頻率:5k
-
(2) 設計功能
- Post / Share a tweet
- Timeline / News Feed
- Follow / Unfollow a user
- Register / Login
- User Profile Display / Edit
-
-
設計可行解:有哪些Service 和Storage,數據流流向是怎樣?將大系統拆分為小服務
-
(1) Service
- User Service
- Tweet Service
- Media Service
- Friendship Service
-
(2) Storage
-
Pull Model: 獲得每個關注對象發的推特,然后在內存中merge
復雜度:O(N) N為關注的好友,為N次DB Reads時間- 缺點:查詢O(N)哥個關注對象的過程是阻塞的,會非常慢
-
Push Model: 為每個用戶見一個news feed list,用戶發一個帖子時,將帖子推送到關注該用戶的news feed list中;關鍵詞: Fanout;所以那個用戶get的時候直接從news feed list中取就行了,不用再查數據庫了;
-
優點:可以將Post tweet到news feed list中的過程變為異步的:不再用戶發帖的過程中執行,無需用戶等待;
-
缺點:不及時,fanout執行可能帶來延遲,
-
數據表設計:News Feed Table
id、owner_id、tweet_id、created_time
- push模型系統結構圖:
-
-
-
-
優化
- (1)加Cache:最慢的部分發生在用戶讀請求時(需要耗費用戶等待時間),在 DB 訪問之前加入Cache :Cache 每個用戶的 Timeline