面試官:如何設(shè)計(jì)一個(gè)高并發(fā)的短鏈系統(tǒng)?
短鏈系統(tǒng)的核心功能,通過(guò)將長(zhǎng)鏈接轉(zhuǎn)換為短字符串的方式,提升鏈接的可讀性和傳播性,適用于消息通知、廣告推廣、社交媒體等場(chǎng)景。
如下圖所示:
圖片
短鏈系統(tǒng)的核心功能有兩個(gè),短鏈跳轉(zhuǎn)和短鏈生成,我們來(lái)一一看下具體實(shí)現(xiàn),最后再講講如何實(shí)現(xiàn)高并發(fā)的短鏈系統(tǒng)。
短鏈跳轉(zhuǎn)的實(shí)現(xiàn)原理
短鏈跳轉(zhuǎn)有HTTP 302和HTTP 301兩種實(shí)現(xiàn)方式,我們來(lái)分別講解一下。
永久重定向(301)
瀏覽器會(huì)緩存第一次請(qǐng)求短鏈所重定向的長(zhǎng)鏈,之后再有相同的短鏈請(qǐng)求,就可以直接從瀏覽器緩存中獲取了,無(wú)須再給短鏈服務(wù)器發(fā)送請(qǐng)求獲取。
StatusCode: 301 Moved Permanently
Location: https://origin.com/long-url?param=value
圖片
臨時(shí)重定向(302)
當(dāng)用戶訪問(wèn)短鏈接的時(shí)候,短鏈服務(wù)器會(huì)返回原始的長(zhǎng)鏈并進(jìn)行HTTP重定向,將其轉(zhuǎn)發(fā)到長(zhǎng)鏈服務(wù)器上,這一切對(duì)于用戶是無(wú)感的。
瀏覽器不會(huì)對(duì)請(qǐng)求短鏈所重定向的長(zhǎng)鏈進(jìn)行緩存,每次都需要請(qǐng)求短鏈服務(wù)器進(jìn)行獲取。
Status Code: 302 Moved Temporarily
Location: https://origin.com/long-url?param=value
圖片
那問(wèn)題來(lái)了,我們到底應(yīng)該選擇HTTP 301還是302進(jìn)行重定向呢?
如果我們的短鏈服務(wù)處于高并發(fā)場(chǎng)景,優(yōu)先選擇HTTP 301永久重定向的方式減少對(duì)服務(wù)的請(qǐng)求數(shù),從而降低服務(wù)器的壓力。
反之,在非高并發(fā)場(chǎng)景且對(duì)訪問(wèn)短鏈服務(wù)有計(jì)數(shù)訴求,那我們可以選擇HTTP 302臨時(shí)重定向。
短鏈生成的實(shí)現(xiàn)原理
接下來(lái),我們來(lái)講講短鏈生成的實(shí)現(xiàn)原理:長(zhǎng)鏈哈希、自增序列、隨機(jī)字符串和預(yù)生成四種方案。
如下圖所示:
圖片
長(zhǎng)鏈哈希
(1)目前常見(jiàn)的哈希算法有MD5、SHA1、MurmurHash等,先將長(zhǎng)鏈通過(guò)這些算法生成一個(gè)哈希值。
如:將長(zhǎng)鏈“https://www.example.com/very/long/url/with/parameters”進(jìn)行哈希計(jì)算,得到結(jié)果“3a6bd3e2560a3d23eea436fcfb7e44c7”。
(2)將生成的結(jié)果字符串進(jìn)行截?cái)?,留下?位短碼,得到結(jié)果“3a6bd3”。
目前,6位短碼已經(jīng)能夠滿足絕大多數(shù)使用場(chǎng)景,因?yàn)?位的短碼可以提供568億種的組合。
(3)將截取后得到的短碼和原始的長(zhǎng)鏈建立一一映射關(guān)系,并存儲(chǔ)在數(shù)據(jù)庫(kù)或緩存中。
這樣,當(dāng)用戶訪問(wèn)短碼對(duì)應(yīng)的短鏈時(shí),系統(tǒng)就可以通過(guò)查詢映射關(guān)系找到原始的長(zhǎng)鏈,并進(jìn)行重定向。
該方案的優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單,但存在哈希碰撞的可能性,在碰撞時(shí)需要在原來(lái)的哈希值上增加隨機(jī)數(shù)再進(jìn)行哈希。
自增序列
(1)目前有兩種主流的實(shí)現(xiàn)方式,通過(guò)雪花算法或數(shù)據(jù)庫(kù)自增ID來(lái)生成唯一數(shù)字ID。
前者則強(qiáng)依賴機(jī)器時(shí)鐘,如果機(jī)器上時(shí)鐘回?fù)?,?huì)導(dǎo)致ID重復(fù)。
后者的ID生成涉及到數(shù)據(jù)庫(kù)操作,性能相對(duì)不高,且引入數(shù)據(jù)庫(kù)會(huì)導(dǎo)致鏈路變長(zhǎng),增加出錯(cuò)概率。
圖片
(2)以進(jìn)制轉(zhuǎn)換的方式將數(shù)字ID進(jìn)行縮短,如:十進(jìn)制數(shù)字1234567890的六十二進(jìn)制為1l3MoK。
(3)將截取得到的短碼和原始的長(zhǎng)鏈建立一一映射關(guān)系,并存儲(chǔ)在數(shù)據(jù)庫(kù)或緩存中。
該方案的優(yōu)點(diǎn)是不存在長(zhǎng)鏈哈希的碰撞問(wèn)題,但如上文所述,也會(huì)根據(jù)實(shí)現(xiàn)方式不同引入其他問(wèn)題。
隨機(jī)字符串
(1)可以通過(guò)取UUID前8位字符的方式生成短碼,將實(shí)現(xiàn)起來(lái)非常簡(jiǎn)單。
import java.util.UUID;
public class GenerateUUID {
public static void main(String[] args) {
// 生成一個(gè)隨機(jī)的 UUID
UUID uuid = UUID.randomUUID();
// 輸出生成的 UUID
String uuidString = uuid.toString();
// 截取前八位
String firstEightChars = uuidString.substring(0, 8);
// 輸出截取的前八位
System.out.println("First eight characters: " + firstEightChars);
}
}
(2)以進(jìn)制轉(zhuǎn)換的方式將數(shù)字ID進(jìn)行縮短,如:十六進(jìn)制數(shù)字5ca877b6的六十二進(jìn)制為k3v6bO。
(3)將截取得到的短碼和原始的長(zhǎng)鏈建立一一映射關(guān)系,并存儲(chǔ)在數(shù)據(jù)庫(kù)或緩存中。
該方案的優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單,通過(guò)JDK自帶的工具類(lèi)即可實(shí)現(xiàn),連jar包都不需要引入,但會(huì)存在數(shù)字ID重復(fù)的問(wèn)題。
預(yù)生成
該方案主要解決上述方案中,臨時(shí)生成哈希值存在哈希碰撞,或臨時(shí)生成數(shù)字ID存在重復(fù)的問(wèn)題。
預(yù)先生成一批短碼并存儲(chǔ)在數(shù)據(jù)庫(kù)中,當(dāng)需要生成短鏈時(shí),直接將未使用的短碼與長(zhǎng)鏈接進(jìn)行關(guān)聯(lián)即可。
當(dāng)然,該方案相比于其他三種方案,實(shí)現(xiàn)起來(lái)更復(fù)雜一些。
實(shí)現(xiàn)高并發(fā)的短鏈系統(tǒng)
以阿里、字節(jié)、拼多多等互聯(lián)網(wǎng)大廠的電商鏈接、短視頻分享為例,每天會(huì)產(chǎn)生幾億條短鏈,訪問(wèn)短鏈的QPS會(huì)達(dá)到幾百萬(wàn)。
如此高的并發(fā)量,對(duì)于短鏈系統(tǒng)承載力是個(gè)巨大的考驗(yàn)。
高并發(fā)短鏈系統(tǒng)架構(gòu)設(shè)計(jì)如下:
圖片
(1)如上文所說(shuō),如果我們的短鏈服務(wù)處于高并發(fā)場(chǎng)景,優(yōu)先選擇HTTP 301永久重定向的方式減少對(duì)服務(wù)的請(qǐng)求數(shù),從而降低服務(wù)器壓力。
(2)雪花算法,每個(gè)服務(wù)器節(jié)點(diǎn)理論上可生成4096000個(gè)不重復(fù)的數(shù)字ID,然后再轉(zhuǎn)換為62進(jìn)制的短鏈。
由于雪花算法不會(huì)出現(xiàn)重復(fù)數(shù)字ID的情況,可以在代碼中省去短鏈判重步驟,提升生成短鏈的吞吐量。
(3)采用Redis + Caffine雙緩存機(jī)制存儲(chǔ)熱點(diǎn)短鏈,由于短鏈不會(huì)出現(xiàn)變更的情況,所以不用考慮數(shù)據(jù)一致性的問(wèn)題。
(4)每天產(chǎn)生幾億條短鏈,換算下來(lái)每秒鐘生成短鏈的峰值TPS可達(dá)到幾萬(wàn),采用分庫(kù)分表的方案來(lái)均攤寫(xiě)操作帶來(lái)的性能瓶頸,Sharding Key為短鏈值。
分片算法:
數(shù)據(jù)庫(kù)ID = 短鏈碼哈希值 % 數(shù)據(jù)庫(kù)數(shù)量
數(shù)據(jù)表ID = 短鏈碼哈希值 / 數(shù)據(jù)庫(kù)數(shù)量 % 數(shù)據(jù)表數(shù)量
另外,短鏈映射表中的數(shù)據(jù)是冷熱分明的,可通過(guò)三個(gè)月或半年進(jìn)行歸檔的方式降低數(shù)據(jù)庫(kù)的存儲(chǔ)壓力。