👉 這是一個或許對你有用的社群
《專案實戰(影片)》:從書中學,往事上“練” 《網際網路高頻面試題》:面朝簡歷學習,春暖花開 《架構 x 系統設計》:摧枯拉朽,掌控面試高頻場景題 《精進 Java 學習指南》:系統學習,網際網路主流技術棧 《必讀 Java 原始碼專欄》:知其然,知其所以然

👉這是一個或許對你有用的開源專案國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERP、CRM、AI 大模型等等功能:
Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud 影片教程:https://doc.iocoder.cn 【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本
引言
今天下午,同事在廁所裡排隊等坑的時候(人多坑少)。想象一下一個場景,我正在一邊排隊,一邊拿著手機撩妹。前面一個同事,拿著手機簡訊轉過頭來和我聊天。
於是,我們就開始討論下面這種短連結的實現原理(沒錯,上廁所也不忘學習!)。

點選其中短連結後,我們會跳到如下地址
http://h5.dangdang.com/mix_20191015_or4x
本文,我們來討論一下其實現原理!基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
專案地址:https://github.com/YunaiV/ruoyi-vue-pro 影片教程:https://doc.iocoder.cn/video/
正文
需求緣起
這裡說一下,為什麼需要短連結?這個簡單,比如大家發微博有字數限制

如果 URL 地址過長,顯然可以寫的關鍵字就越少!
再比如發簡訊如果簡訊內容過長,那麼一條簡訊就要拆成兩條發,浪費錢!
因此採用短連結,不僅節約資源,還十分美觀!
請求流程
首先,我們先看看噹噹的短連結
http://dwz.win/nXR
它是由兩個部分組成
-
http://dwz.win
:短連結系統的域名地址 -
nXR
:請求引數
請求
http://dwz.win/nXR
地址後,返回狀態如下所示
於是,我們可以推斷出,敲下
http://dwz.win/nXR
地址後,發生了什麼呢?
這裡渣渣煙就要多嘴一句了。上圖所示短連結系統,返回的狀態可以為 301 或者 302,只是噹噹網用的是 301。
這裡我要說一下,大家應該明白
30X
狀態,在 HTTP 協議中,代表的是重定向的狀態。那麼301和302區別在哪呢,繼續往下看。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
;
這麼做,也不是不行,有兩個缺點你要評估能不能接受!
-
(1)如果資料比較大,比如幾百億,你的 url 地址依然過長 -
(2)你的資料具有規律性,別人用一個簡單的指令碼就可以遍歷出你的跳轉地址!
為了解決上面的兩個缺點,我們增加一個列,用來儲存 key 值。此時表結構如下
列名 | 說明 |
---|---|
id | BIGINT,自增主鍵 |
key | 短串,需要加唯一索引 |
url | 長地址,也就是需要跳轉的原地址 |
我們為了縮短 id 的長度呢,一般可以這麼做。由於我們的短連結是由 a-z、A-Z 和 0-9 共 62 個字元可以選擇。因此,我們可以將十進位制的數字 id,轉換為一個 62 進位制的數,例如 201314 就可以轉換為 Qn0。演算法如下
privatestaticfinal
String BASE =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
;
publicstatic 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 做為分片鍵,存在兩個問題!
-
使用者請求的 key,需要做一個逆運算推算回 ID,然後根據 ID,再去對應表裡去找,增加響應時間。 -
根據選擇的隨機演算法不同,key 不一定能夠推算回 ID 值。這種情況下,只能每張表去查,更慢。
所以用 key 做分片鍵,再適合不過了。拿到使用者請求的 KEY 後,直接定位到對應的表裡將 url 取出即可。
(2)讀寫分離
這種系統顯然,讀遠大於寫。建議可以考慮做讀寫分離。
(3)引入快取
假設,我們在一個時間,給手機推送簡訊連結的簡訊後。顯然,後面的一段時間內,對該短連結的請求量會大大提升。沒有必要每次都去資料庫查詢,因此可以引入 redis 快取。
(4)全域性發號器
用其他演算法行不行 ?可以。這裡只是要一個全域性唯一 ID 而已。自己要估算好,使用其他演算法所帶來的效能影響。以及採用其他演算法,會不會造成生成的生成的 ID 過於規律。
(5)防攻擊
做好被惡意攻擊的準備,防止自增 ID 的值,被全部耗光
歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,“長按”或“掃描”下方二維碼噢:

星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。





文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)