原文鏈接:
https://www.educative.io/courses/grokking-the-system-design-interview/m2ygV4E81AR
編者注:本文以一道經典的系統設計面試題:《如何設計TinyURL》的參考答案和解析爲例,幫助讀者更深入地瞭解在系統需求分析和設計中,需要考慮的各個方面的細節。
本文將爲大家詳細講解如何設計一個類似於TinyURL的URL縮短服務。URL縮短服務提供一個非常短小的URL以代替原來的可能較長的URL,將長的URL地址縮短。
類似的服務有:bit.ly,goo.gl,qlink.me,等等。
一、爲什麼需要URL縮短服務?
URL縮短服務是用來爲長URL創建簡短別名的。我們稱這些縮短的別名爲“短網址”。當用戶點擊這些短網址時,能夠重定向到原始的URL。相較於原始URL,短網址在顯示、打印、發送消息或微博時能夠節省大量空間。另外,用戶輸錯短網址的可能性也比較小。
舉個例子,如果我們通過TinyURL來縮短下面這個網址:
https://www.educative.io/collection/page/5668639101419520/5649050225344512/5668600916475904/
我們可以得到以下的短網址:
http://tinyurl.com/jlg8zpc
上面這個短網址的長度幾乎只有實際URL的三分之一。
URL縮短用於優化跨設備的鏈接,通過跟蹤單個鏈接來分析受衆和活動性能,並隱藏關聯的原始URL。
如果你以前沒有使用過http://tinyurl.com/這個網站,請嘗試設計並創建一個新的網址縮短系統,然後,花一些時間瀏覽它們提供的各種服務選項。這會幫助你更好地理解本章節的內容。
二、系統的需求和目標
設計的URL縮短系統應符合以下需求:
功能性需求:
1、給定一個原始URL,該系統能夠生成一個較短且唯一的短網址。這個短網址的長度應該足夠短,以便輕鬆複製和粘貼到應用程序中。
2、當用戶訪問一個短網址時,該系統應該將它們重定向到原始鏈接。
3、用戶可以爲他們的URL選擇一個自定義的短網址。
4、在標準的默認時間間隔後,對應的短網址過期。過期時間允許用戶設置。
非功能性需求:
1、該系統必須是高度可用的。因爲一旦我們的服務宕機,那麼所有的URL重定向都會失敗。
2、URL重定向應該在最小延遲的情況下實時進行。
3、短網址應該是不可預測
擴展需求:
1、能夠進行分析;例如,重定向發生了多少次。
2、其他系統可以通過REST API訪問我們的服務。
三、容量估計及限制
設計的系統將包含大量讀取操作。與新的URL縮短相比,將會有大量的重定向請求。假設讀和寫的比率爲100:1。
流量估計:
假設每個月將有5億個新的短網址產生,讀/寫比率爲100:1的話,預計在同一時期將有500億個重定向請求:
100 * 5 億 ≥ 500 億
對於我們設計的系統,每秒查詢數(QPS)又是多少呢?每秒新的短網址數:
5 億 / (30 天 * 24 小時 * 3600 秒 ) ≌ 200 URLs/s
考慮到100:1的讀/寫比例,每秒URL重定向請求數將是:
100 * 200 URLs/s = 20 K/s
存儲估計:
假設將每個短網址請求存儲5年,每個月將有5 億個新的短網址產生,由此可以得出,預計存儲的對象總數將達到300億個:
5 億 * 5 年 * 12 月 = 300 億
假設每個存儲對象的大小大約是500 字節(這只是一個大致的估計——我們稍後將深入研究它)。那我們需要15 TB 的總存儲:
300 億 * 500 字節 = 15 TB
帶寬估計:
對於寫請求,前面我們計算出,預計每秒有200個新短網址產生,由此得出,總輸入數據將爲100 KB/s:
200 * 500 字節 ≌ 100 KB/s
對於讀請求,前面我們計算出,預計每秒URL重定向請求是2 萬個,由此得出,總輸出數據爲10MB/s:
2 萬 * 500 字節 ≌10 MB/s
內存估計:
如果想緩存一些經常訪問的熱門URL,那需要多少內存來存儲它們呢?
這裏,我們遵循80-20規則,也就是說20%的URL產生80%的流量,我們想要緩存這20%的熱門URL。
由於每秒有2萬個重定向請求,那麼每天收到的重定向請求有17億個:
2 萬 * 3600 秒 * 24 小時 ≌ 17 億
緩存其中20%的請求所需要的內存則爲170 GB:
0.2 * 17 億 * 500 字節 ≌ 170GB
這裏需要注意一點,由於會有許多重複的請求(也就是相同的URL),因此,實際內存使用量將少於170 GB。
高級別估計:
假設每月有5億個新URL,讀/寫比例100:1,下面是對此服務的估計概要:
四、系統API
一旦確定了系統的需求,那麼定義系統API總是一個好主意。它應該明確地說明我們對系統的期望。
我們可以使用SOAP或REST API來公開服務的功能。以下是用於創建和刪除URL的API的定義:
<section style="margin-top: 10px;margin-bottom: 20px;"><code class=""><span style="font-size: 15px;">createURL(api_dev_key, original_url, custom_alias=None, user_name=None, expire_date=None)</span></code></section>
相關參數:
api_dev_key(字符串):註冊賬戶的API開發人員密鑰。除此之外,它還將用於根據分配的配額限制用戶。
original_url(字符串):要縮短的原始URL。
custom_alias(字符串):URL的可選自定義鍵。
user_name(字符串):可選的用戶名,用於編碼。
expire_date(字符串):可選過期日期的縮短URL。
返回:(字符串)
成功插入將返回短網址;否則,將返回錯誤代碼。
<section style="margin-top: 10px;margin-bottom: 20px;"><code class=""><span style="font-size: 15px;">deleteURL(api_dev_key, url_key)</span></code></section>
其中“url_key”表示的是要檢索的縮短URL的字符串。成功刪除將返回“URL Removed”。
那我們要如何發現和防止濫用呢?惡意用戶可以通過使用當前設計中的所有URL密鑰讓我們失去業務。爲了防止濫用,我們可以通過api_dev_key來限制用戶。每個api_dev_key可以在某個時間段內限制URL創建和重定向的數量(每個開發人員密鑰可以設置爲不同的持續時間)。
五、數據庫設計
對於我們將要存儲的數據,對其性質做一些觀察:
1、需要存儲數十億的記錄。
2、存儲的每個對象都很小(小於1K)。
3、除了存儲創建URL的用戶之外,記錄之間沒有任何關係。
4、需要大量的讀取操作。
數據庫架構:
我們需要兩個表:一個用於存儲有關URL映射的信息,另一個用於存儲創建短網址的用戶數據。
那我們應該使用什麼樣的數據庫呢?因爲我們預計存儲數十億的數據,而且我們不需要使用對象之間的關係,所以像DynamoDB,Cassandra或者Riak這樣的通過key-value形式存儲的NoSQL是更好的選擇。選擇NoSQL也更容易進行擴展。
(未完待續)