如何設計一個像百度短網址同樣的服務,一個生成短網址、將短網址定向到原始URL的服務。redis
首先,須要一個一對一的映射表,去獲取短URL,而後根據他恢復出完整的URL。這將會涉及到將這些數據保存到數據庫。數據庫
咱們要考慮下面這些問題:服務器
咱們假設咱們設計的系統能夠提供超過10000億的URL,若是咱們以從62個字符[a-z,A-Z,0-9]
中選取n
個做爲短網址,咱們能夠存儲62^n
個網址。因此,在知足條件的狀況下,咱們選擇最小的n
。對於咱們的需求來講,咱們取n=7
,這樣能夠提供短網址的個數是62^7 ~= 35000億
。分佈式
一切從簡,咱們假定短網址相似http://dwz.com/<alias_hash>
,其中alias_hash
是一個計算過得字符串。函數
最開始呢,咱們把全部的映射存到一個單機數據庫,直接生成一個隨機的長度爲7的字符串alias_hash
做爲映射中的鍵ID
。性能
因此,咱們只須要存儲<ID, URL>
。當使用者輸入完整網址http://www.google.com
,系統生成一個像abcd123
同樣的長度爲7的隨機字符串做爲ID
,存儲到數據庫中就是<abcd123, http://www.google.com>
。google
使用的時候,訪問http://dwz.com/abcd123
,系統搜索IDabcd123
,找到後重定向到http://www.google.com
。url
咱們不能保證經過長URL生成alias_hash
的惟一性。生成alias_hash
的時候可能會發生衝突(2個長URL映射到同一個短URL),但咱們須要每一個長URL生成的短URL都是惟一的,這樣咱們才能根據短URL定位到惟一的長URL,可是計算alias_hash
的函數是單向函數。設計
這裏是否是單向函數其實不重要,咱們不須要根據短網址再次作計算,只須要搜索,不知道做者爲何這麼寫。
一個很簡單但依然高效的方案,創建這樣的數據表:code
Table Tiny_Url( ID : int PRIMARY_KEY AUTO_INC, Original_url : varchar, Short_url : varchar )
其中自增主鍵ID
用來作這樣的轉換:(ID, 10) <==> (short_url, BASE)
。使用者隨時插入一個長URL,會返回最新的插入ID
,把他轉化爲短URL標識,保存這個短URL標識,並返回給使用者。
ID
和短URL的互相轉換)string idToShortURL(long int n) { // Map to store 62 possible characters char map[] = "abcdefghijklmnopqrstuvwxyzABCDEF" "GHIJKLMNOPQRSTUVWXYZ0123456789"; string shorturl; // Convert given integer id to a base 62 number while (n) { shorturl.push_back(map[n%62]); n = n/62; } // Reverse shortURL to complete base conversion reverse(shorturl.begin(), shorturl.end()); return shorturl; } // Function to get integer ID back from a short url long int shortURLtoID(string shortURL) { long int id = 0; // initialize result // A simple base conversion logic for (int i=0; i < shortURL.length(); i++) { if ('a' <= shortURL[i] && shortURL[i] <= 'z') id = id*62 + shortURL[i] - 'a'; if ('A' <= shortURL[i] && shortURL[i] <= 'Z') id = id*62 + shortURL[i] - 'A' + 26; if ('0' <= shortURL[i] && shortURL[i] <= '9') id = id*62 + shortURL[i] - '0' + 52; } return id; }
若是咱們的服務要處理大量數據,分佈式存儲能夠提升咱們的吞吐量。思路也很簡單,計算出原始URL的hash值,而後去對應的及其存儲,而後就和單機狀況同樣了。一般使用一致性hash路由到集羣中對應的節點。
下面的例子是僞代碼:
hash_val
。hash_val
定位一致性hash環上的機器。idToShortURL()
獲取短URLhash_val
和短URL做爲咱們最終短URL(長度=8),返回給使用者。hash_val
。hash_val
定位到機器。
這裏可能有問題,坐着的意思多是最多62臺機器,應該是第一個字符是標識機器的,值多是
[a-z,A-Z,0-9]
中的一個。
看了評論,這裏有個問題,若是某臺機器掛了,摘掉以後,須要將該機器上的數據複製到一致性hash環上的下一臺機器的時候,會發生衝突,因此仍是使用redis之類的生成全局惟一的
ID
,直接落到環上對應的機器,這樣設計更好。
這裏我想深刻討論的是使用GUID
(全局惟一標識)做爲ID
,與增量ID
相比有哪些優缺點?
若是你深刻insert/query
處理語句的話,你會發現使用隨機字符串做爲ID
可能會犧牲一小部分性能。特別是當已經有無數的記錄的時候,插入是很昂貴的操做,數據庫須要尋找合適的頁存儲插入的ID
。可是,使用增量的ID
,插入會簡單不少,直接找到最後的頁就好了。