今天下午,煙哥和同事在廁所裏排隊等坑的時候(人多坑少)。想象一下一個場景,我正在一邊排隊,一邊拿着手機撩妹。前面一個同事,拿着手機短信轉過頭來和我聊天。
因而,咱們就開始討論下面這種短連接的實現原理(沒錯,上廁所也不忘學習!)。
redis
點擊其中短連接後,咱們會跳到以下地址
http://h5.dangdang.com/mix_20191015_or4x
本文,咱們來討論一下其實現原理!算法
這裏說一下,爲何須要短連接?這個簡單,好比你們發微博的時候
數據庫
若是URL地址過長,顯然能夠寫的關鍵字就越少!瀏覽器
再好比發短信若是短信內容過長,那麼一條短信就要拆成兩條發,浪費錢!緩存
所以採用短連接,不只節約資源,還十分美觀!服務器
首先,咱們先看看噹噹的短連接http://dwz.win/nXR
它是由兩個部分組成
http://dwz.win
:短連接系統的域名地址
nXR
:請求參數
請求http://dwz.win/nXR
地址後,返回狀態以下所示
app
因而,咱們能夠推斷出,敲下http://dwz.win/nXR
地址後,發生了什麼呢?
性能
這裏渣渣煙就要多嘴一句了。上圖所示短連接系統,返回的狀態能夠爲301或者302,只是噹噹網用的是301。
這裏我要說一下,你們應該明白30X
狀態,在HTTP協議中,表明的是重定向的狀態。
301表明什麼?
301表明的是永久重定向。什麼意思呢?
對於GET請求, 301跳轉會默認被瀏覽器cache。也就是說,用戶第一次訪問某個短連接後,若是服務器返回301狀態碼,則這個用戶在後續屢次訪問同一短連接地址,瀏覽器會直接請求跳轉地址,而不會再去短連接系統上取!學習
這麼作優勢很明顯,下降了服務器壓力,可是沒法統計到短連接地址的點擊次數。優化
302表明什麼?
302表明的是臨時定向。什麼意思呢?
對於GET請求, 302跳轉默認不會被瀏覽器緩存,除非在HTTP響應中經過 Cache-Control 或 Expires 暗示瀏覽器緩存。所以,用戶每次訪問同一短連接地址,瀏覽器都會去短連接系統上取。
這麼作的優勢是,可以統計到短地址被點擊的次數了。可是服務器的壓力變大了。
下面說最關鍵的一段,怎麼將http://h5.dangdang.com/mix_20191015_or4x
壓縮爲nXR
字符
首先呢,咱們須要一張表來存儲,長短連接間的映射關係。表結構以下
列名 | 說明 |
---|---|
id | BIGINT,自增主鍵 |
url | 長地址,也就是須要跳轉的原地址 |
好的,假設咱們此時表裏的數據以下
id | url |
---|---|
1 | http://h5.dangdang.com/mix_20191015_or4x |
2 | http://h5.dangdang.com/mix_20191102_ad3x |
咱們此時拿自增id做爲短連接的key。假設域名http://dwz.win
是短連接系統,也就是說請求:
(1)http://dwz.win/1
會跳轉http://h5.dangdang.com/mix_20191015_or4x
;
(2)http://dwz.win/2
會跳轉http://h5.dangdang.com/mix_20191102_ad3x
;
這麼作,也不是不行,有兩個缺點你要評估能不能接受!
爲了解決上面的兩個缺點,咱們增長一個列,用來存儲key值。此時表結構以下
列名 | 說明 |
---|---|
id | BIGINT,自增主鍵 |
key | 短串,須要加惟一索引 |
url | 長地址,也就是須要跳轉的原地址 |
咱們爲了縮短id的長度呢,通常能夠這麼作。因爲咱們的短連接是由 a-z、A-Z 和 0-9 共 62 個字符能夠選擇。所以,咱們能夠講十進制的數字id,轉換爲一個62進制的數,例如201314就能夠轉換爲Qn0。
算法以下
private static final String BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static String toBase62(long num) { StringBuilder sb = new StringBuilder(); do { int i = (int) (num % 62); sb.append(BASE.charAt(i)); num /= 62; } while (num > 0); return sb.reverse().toString(); }
另外,咱們須要引入一個全局發號器,一直返回全局自增的ID。至關於,咱們的短連接系統先去請求這個全局自增ID,而後將全局自增ID轉換爲62進制的數,做爲key。
接下來,解決第二個問題!數據具備規律性的問題。畢竟你轉換爲62進制後,只是解決了數據過長的問題,數據規律性問題仍是沒解決。
所以,咱們須要引入一個隨機算法。那麼此時,你的考慮點在於,你是否要根據key值,反推出全局id值!來抉擇不一樣的隨機算法!
(1)不但願反推出全局ID
OK,那就用一個洗牌算法,打亂算出的值。好比十進制的201314就能夠轉換爲Qn0。而後再使用洗牌算法,能夠返回n0Q、Q0n....其中之一。可是會有必定概率衝突,多洗幾回就行。
(2)但願反推出全局ID
OK,那就在獲得Qn0這個數字後,將其轉換爲二進制數。而後在固定位,第五位,第十位...(等等)插入一個隨機值便可。
至於如何反推也很簡單,你拿到短連接key後,將固定位的數字去除,再轉換爲十進制便可。
講到這裏,就基本將key如何生成的邏輯講清楚了。那麼用戶在點擊短連接的時候,例如地址http://dwz.win/nXR
,短連接系統解析出key爲nXR,根據惟一索引去表中將nXR對應的url返回便可。
(1)分庫分表
若是這個系統是放在公網,給你們使用的。建議上來就分庫分表,數據量過1000萬是很容易的。這裏涉及到一個問題,拿全局發號器給的自增id作分片健,仍是拿轉換後的key作分片鍵。
顯然,用轉換後的key作分片鍵會更容易一些。若是用ID作爲分片鍵,存在兩個問題!
(1)用戶請求的key,須要作一個逆運算推算回ID,而後根據ID,再去對應表裏去找,增長響應時間。
(2)根據選擇的隨機算法不一樣,key不必定可以推算回ID值。這種狀況下,只能每張表去查,更慢。
因此用key作分片鍵,再適合不過了。拿到用戶請求的KEY後,直接定位到對應的表裏將url取出便可。
(2)讀寫分離
這種系統顯然,讀遠大於寫。建議能夠考慮作讀寫分離。
(3)引入緩存
假設,咱們在一個時間。給手機推送短信連接的短信後。顯然,後面的一段時間內,對該短連接的請求量會大大提高。沒有必要每次都去數據庫查詢,所以能夠引入redis緩存。
(4)全局發號器用其餘算法行不行
能夠。這裏只是要一個全局惟一ID而已。本身要估算好,使用其餘算法所帶來的性能影響。以及採用其餘算法,會不會形成生成的生成的ID過於規律。
(5)防攻擊
作好被惡意攻擊的準備,防止自增ID的值,被所有耗光。
很久沒寫這種設計型的文章了。畢竟是我和同事在廁所蹲坑排隊之餘,在那閒聊出來的。最後,因爲煙哥過於專一和同事聊技術,致使後來我回復那個妹紙的時候已經沒有了下文,注孤生!
但願你們有所收穫!