偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

面試官:如何設(shè)計(jì)一個(gè)十億級的 URL 短鏈系統(tǒng)?

開發(fā) 后端
在后端面試中,系統(tǒng)設(shè)計(jì)題是檢驗(yàn)一位工程師架構(gòu)思維和技術(shù)深度的試金石。其中,“設(shè)計(jì)一個(gè)短鏈系統(tǒng)”可以說是最高頻的題目之一。

在后端面試中,系統(tǒng)設(shè)計(jì)題是檢驗(yàn)一位工程師架構(gòu)思維和技術(shù)深度的試金石。其中,“設(shè)計(jì)一個(gè)短鏈系統(tǒng)”可以說是最高頻的題目之一。

這個(gè)問題看似簡單——不就是把長網(wǎng)址變短嗎?但如果你真的這么認(rèn)為,那可能就危險(xiǎn)了。一個(gè)看似基礎(chǔ)的功能背后,隱藏著海量數(shù)據(jù)存儲、高并發(fā)處理、服務(wù)高可用等一系列復(fù)雜的工程挑戰(zhàn)。面試官拋出這個(gè)問題,正是想看看你如何抽絲剝繭,從一個(gè)簡單的需求出發(fā),逐步構(gòu)建出一個(gè)穩(wěn)健、高效、可擴(kuò)展的十億級系統(tǒng)。

今天,秀才就帶大家來模擬一次真實(shí)的面試過程,一步步剖析如何完美地回答這個(gè)問題,讓你在面試中脫穎而出。

一、什么是短鏈系統(tǒng)?

首先,我們得確保和面試官在同一個(gè)“頻道”上。短鏈系統(tǒng),顧名思義,就是將一個(gè)冗長的URL(例如一篇新聞的鏈接)轉(zhuǎn)換成一個(gè)非常短的URL。當(dāng)用戶訪問這個(gè)短鏈接時(shí),系統(tǒng)會自動(dòng)將他重定向到原始的長鏈接地址。

這個(gè)過程的核心在于“生成”和“重定向”。

上圖清晰地展示了整個(gè)交互流程:

  • 生成:應(yīng)用程序?qū)⒃奸LURL發(fā)送給短鏈系統(tǒng)。
  • 返回:短鏈系統(tǒng)生成一個(gè)唯一的短URL,并返回給應(yīng)用程序。
  • 訪問:用戶點(diǎn)擊短URL。
  • 重定向:瀏覽器訪問短鏈系統(tǒng),系統(tǒng)查詢到原始長URL后,返回一個(gè)HTTP 302重定向響應(yīng),瀏覽器隨即訪問原始的URL目標(biāo)服務(wù)器。

二、為什么我們需要URL短鏈系統(tǒng)?

明確了“是什么”,接下來就要理解“為什么”。在面試中,能清晰地闡述一個(gè)技術(shù)的業(yè)務(wù)價(jià)值,是體現(xiàn)你商業(yè)思考和產(chǎn)品思維的加分項(xiàng)。

URL短鏈系統(tǒng)的價(jià)值主要體現(xiàn)在以下幾個(gè)方面:

  • 美觀與便捷:在社交媒體(如微博)、短信、二維碼等對字符長度有限制的場景下,短鏈接是剛需。它更易于分享、打印和口頭傳播,用戶也更不容易輸錯(cuò)。
  • 數(shù)據(jù)追蹤與分析:這是短鏈接一個(gè)非常核心的商業(yè)價(jià)值。通過在短鏈接的重定向上增加統(tǒng)計(jì)邏輯,我們可以追蹤到鏈接的點(diǎn)擊次數(shù)、點(diǎn)擊來源、用戶地域分布、設(shè)備信息等,為運(yùn)營決策和廣告效果衡量提供精準(zhǔn)的數(shù)據(jù)支持。
  • 功能擴(kuò)展:短鏈接可以作為一層中間代理,在這層代理上我們可以實(shí)現(xiàn)很多高級功能,比如鏈接的有效期控制、訪問密碼保護(hù)、設(shè)備跳轉(zhuǎn)控制(移動(dòng)端和PC端訪問跳轉(zhuǎn)到不同頁面)等。
  • 隱藏原始鏈接:在某些推廣場景下,可以隱藏真實(shí)的、帶有復(fù)雜參數(shù)的原始URL。

比如,一個(gè)很長的商品鏈接:https://www.designsystem/shyfeawou/csline/ccosifhyew/online-course/clare-system-design-interview

可以被縮短為:https://tinyurl.com/vzxt58la

長度縮短了近三分之二,無論是在哪個(gè)場景下使用,都顯得清爽利落。

三、面試實(shí)戰(zhàn)指南

好了,背景知識鋪墊完畢,現(xiàn)在正式進(jìn)入面試環(huán)節(jié)。系統(tǒng)設(shè)計(jì)題的回答,切忌一上來就拋出最終方案。一個(gè)優(yōu)秀的回答過程,應(yīng)該像一次與面試官共同探索的旅程,充滿互動(dòng)和逐步深入的思考。

1. 需求分析:一切設(shè)計(jì)的起點(diǎn)

面試官:“我們來聊聊系統(tǒng)設(shè)計(jì)吧。如果要你設(shè)計(jì)一個(gè)高性能的短鏈系統(tǒng),你會怎么入手?”

這是一個(gè)開放性問題,千萬不要直接回答:“我會用Redis、用哈希算法……”。正確的起手式是確認(rèn)需求。這能體現(xiàn)你嚴(yán)謹(jǐn)?shù)墓こ趟仞B(yǎng)。

你可以這樣回應(yīng):

“在開始設(shè)計(jì)之前,我想先和您明確一下系統(tǒng)的具體需求和目標(biāo)范圍,這樣我們的討論會更有針對性。比如,我們需要支持哪些核心功能?對系統(tǒng)的性能和可用性有什么樣的要求?”

通過這樣的提問,你就把問題拋給了面試官,也展示了你的專業(yè)性。并且更重要的是,可以從面試官那里確認(rèn)出這個(gè)系統(tǒng)的一個(gè)大概的需求范圍。假設(shè)經(jīng)過溝通,你們明確了以下需求:

(1) 功能性需求:

  • 生成短鏈:輸入一個(gè)長URL,系統(tǒng)能生成一個(gè)唯一的、更短的別名(短鏈接)。
  • 支持自定義:用戶可以選擇性地為他們的URL指定一個(gè)有意義的自定義短鏈接。
  • 重定向:訪問短鏈接時(shí),系統(tǒng)必須能準(zhǔn)確、快速地重定向到原始的長鏈接。
  • 過期機(jī)制:鏈接可以設(shè)置過期時(shí)間,過期后失效。

(2) 非功能性需求:

  • 高可用:系統(tǒng)必須7x24小時(shí)可用。因?yàn)橐坏┓?wù)宕機(jī),所有短鏈接都將失效,這在很多場景下是災(zāi)難性的。
  • 高性能:重定向過程必須延遲極低,用戶幾乎無感知。
  • 唯一且不可預(yù)測:生成的短鏈接必須是唯一的,并且不能被輕易地猜到規(guī)律,以防被惡意遍歷。

(3) 擴(kuò)展性需求:

  • 數(shù)據(jù)分析:需要支持統(tǒng)計(jì)短鏈接的訪問數(shù)據(jù)。
  • 開放API:系統(tǒng)應(yīng)提供REST API,方便其他服務(wù)接入。

2. 容量預(yù)估:用數(shù)據(jù)指導(dǎo)架構(gòu)

需求明確了,下一步就要進(jìn)行一個(gè)容量預(yù)估了。對于后端設(shè)計(jì)者而言,必須要做一個(gè)容量的估算,這樣才方便我們選定合適的方案。

“需求很清晰了。接下來,我想做一個(gè)簡單的容量預(yù)估,這有助于我們判斷系統(tǒng)的量級,從而選擇合適的技術(shù)方案。”

這是展示你架構(gòu)設(shè)計(jì)能力的關(guān)鍵一步。沒有數(shù)據(jù)支撐的設(shè)計(jì)都是空中樓閣。

(1) 流量估算:假設(shè)我們是一個(gè)快速發(fā)展的業(yè)務(wù),預(yù)計(jì)每月新增5億個(gè)短鏈接。短鏈系統(tǒng)的讀寫比通常非常懸殊,讀取(重定向)遠(yuǎn)多于寫入(生成)。我們假設(shè)讀寫比例為 100:1。

  • 寫入QPS:每月5億次寫入,那么每秒的寫入請求大約是 5億 / (30天 * 24小時(shí) * 3600秒) ≈ 200次/秒。
  • 讀取QPS:根據(jù)100:1的讀寫比,讀取請求大約是 200 * 100 = 2萬次/秒。這個(gè)并發(fā)量已經(jīng)不低了。

(2) 存儲估算:假設(shè)每個(gè)短鏈接數(shù)據(jù)(包括長URL、短URL、創(chuàng)建時(shí)間、用戶ID等)我們存儲5年。

  • 總記錄數(shù):5億/月 * 12個(gè)月/年 * 5年 = 300億。這是一個(gè)非常龐大的數(shù)字。
  • 單條記錄大?。何覀兇致怨烙?jì)每條記錄為500字節(jié)。
  • 總存儲空間:300億 * 500字節(jié) ≈ 15TB。這個(gè)存儲需求對于單機(jī)數(shù)據(jù)庫來說是無法承受的。

(3) 帶寬估算:

  • 入口帶寬(寫):200次/秒 * 500字節(jié)/次 ≈ 100 KB/s。
  • 出口帶寬(讀):2萬次/秒 * 500字節(jié)/次 ≈ 10 MB/s。

(4) 緩存估算:為了提升讀取性能,緩存是必不可少的。根據(jù)二八原則,80%的訪問量集中在20%的熱點(diǎn)鏈接上。

  • 每日總請求數(shù):2萬次/秒 * 3600秒/小時(shí) * 24小時(shí)/天 ≈ 17億次。
  • 緩存目標(biāo):我們需要緩存這17億次請求中的20%。
  • 所需內(nèi)存:17億 * 20% * 500字節(jié) ≈ 170GB。

將這些估算結(jié)果匯總成一個(gè)表格,展示給面試官,會顯得非常專業(yè):

指標(biāo)

估算值

新增URL(寫QPS)

~200/s

URL重定向(讀QPS)

~20K/s

入口帶寬

100KB/s

出口帶寬

10MB/s

5年總存儲

15TB

緩存所需內(nèi)存

170GB

通過估算,我們可以看到,這個(gè)系統(tǒng)面臨的是高并發(fā)讀取和海量數(shù)據(jù)存儲兩大挑戰(zhàn)。這將是我們后續(xù)架構(gòu)設(shè)計(jì)的核心出發(fā)點(diǎn)。

3. 系統(tǒng)接口定義(API Design)

在宏觀設(shè)計(jì)之前,先定義微觀的接口,是一種自底向上的優(yōu)秀設(shè)計(jì)習(xí)慣。這有助于我們厘清系統(tǒng)的邊界和能力。

接下來我們可以設(shè)計(jì)一套RESTful API來暴露服務(wù)能力。主要包括以下幾個(gè)接口:

(1) 創(chuàng)建短鏈 API

請求路由:GET /{shortened_url}

請求參數(shù):

參數(shù)

釋義

original_url

需要縮短的原始長網(wǎng)址(字符串,必填)

custom_alias

用戶希望指定的短網(wǎng)址自定義別名(字符串,可選)

expiration_date

短網(wǎng)址應(yīng)過期的日期和時(shí)間(時(shí)間戳,可選)

user_id

創(chuàng)建縮短 URL 的用戶 ID(如果支持用戶賬戶功能)(字符串,可選)

響應(yīng)參數(shù):

參數(shù)

釋義

shortened_url

由服務(wù)生成的縮短后 URL(字符串)

creation_date

URL 被縮短時(shí)的日期和時(shí)間(時(shí)間戳)

expiration_date

短網(wǎng)址的過期日期(時(shí)間戳,如提供)

(2) 重定向 API

將用戶從短鏈接重定向至原始長網(wǎng)址。

請求路由:GET /{shortened_url}

請求參數(shù):

參數(shù)

釋義

shortened_url

需要解析為原始網(wǎng)址的短鏈接(字符串,必填)

響應(yīng):重定向至 original_url。

(3) 數(shù)據(jù)分析接口

提供短鏈接的詳細(xì)分析數(shù)據(jù),包括點(diǎn)擊次數(shù)和用戶人口統(tǒng)計(jì)信息。

請求路由:GET /analytics/{shortened_url}

請求參數(shù):

參數(shù)

釋義

shortened_url

需要獲取分析數(shù)據(jù)的短鏈接(字符串,必填)

start_date

用于篩選分析數(shù)據(jù)的開始日期(時(shí)間戳,可選)

end_date

用于篩選分析數(shù)據(jù)的結(jié)束日期(時(shí)間戳,可選)

響應(yīng)參數(shù):

參數(shù)

釋義

click_count

該短鏈接被點(diǎn)擊的總次數(shù)(整數(shù))

unique_clicks

點(diǎn)擊該短鏈接的唯一用戶數(shù)(整數(shù))

referring_sites

為該短鏈接帶來流量的來源網(wǎng)站(列表)

location_data

點(diǎn)擊該網(wǎng)址的用戶地理分布(地圖)

device_data

點(diǎn)擊 URL 所使用的設(shè)備(移動(dòng)設(shè)備、桌面設(shè)備等)的細(xì)分(映射)

(4) 網(wǎng)址管理 API

請求路由: GET /user/urls

請求參數(shù):

參數(shù)

釋義

user_id

請求其短網(wǎng)址的用戶 ID(字符串, 必需)

page

分頁結(jié)果的頁碼(整數(shù), 可選)

page_size

每頁的結(jié)果數(shù)量(整數(shù),可選)

響應(yīng)參數(shù):

參數(shù)

釋義

urls

用戶縮短的 URL 列表,包括創(chuàng)建日期和過期日期等元數(shù)據(jù)(列表)

(5) 刪除短鏈接 API

請求路由: DELETE /{shortened_url}

請求參數(shù):

參數(shù)

釋義

shortened_url

需要?jiǎng)h除的短鏈接(字符串,必填)

user_id

請求刪除的用戶 ID(字符串,必填)

響應(yīng)參數(shù):

參數(shù)

釋義

status

刪除確認(rèn)信息或操作失敗時(shí)的錯(cuò)誤提示(字符串)

面試官:“接口設(shè)計(jì)得不錯(cuò)。但有個(gè)問題,如果惡意用戶瘋狂調(diào)用創(chuàng)建接口,把我們生成的短碼耗盡了怎么辦?”。這是一個(gè)很好的追問,考察你對系統(tǒng)安全性的思考。

“如果不加限制,面對惡意調(diào)用,短url肯定很快會被耗盡。所以我們需要加入防濫用機(jī)制。核心手段是API速率限制。我們可以基于用戶ID或IP地址進(jìn)行限流,比如,限制每個(gè)IP每分鐘只能創(chuàng)建10個(gè)短鏈接。對于未登錄的匿名用戶,這個(gè)限制可以更嚴(yán)格?!?/p>

4. 數(shù)據(jù)庫設(shè)計(jì)

基于前面的需求和接口,我們可以設(shè)計(jì)出數(shù)據(jù)庫的表結(jié)構(gòu)。

“考慮到300億的記錄量和高并發(fā)讀取的需求,關(guān)系型數(shù)據(jù)庫可能不是最佳選擇。因?yàn)榉謳旆直頃浅?fù)雜,而且我們數(shù)據(jù)之間的關(guān)系很簡單。我更傾向于使用NoSQL數(shù)據(jù)庫,比如AWS的DynamoDB、阿里云的Table Store (OTS) 或者開源的Riak,它們的水平擴(kuò)展能力更強(qiáng)。”

我們可以設(shè)計(jì)以下幾張表(以NoSQL的寬表模型為例):

(1) URL表 (url_mapping)

  • Hash (Partition Key): 短鏈碼,例如 vzet59pa。這是我們的主鍵,用于快速查找。
  • OriginalURL: 原始長鏈接。
  • CreationDate: 創(chuàng)建時(shí)間。
  • ExpirationDate: 過期時(shí)間。
  • UserID: 創(chuàng)建者ID。

(2) 用戶表 (users)

  • UserID (Partition Key): 用戶ID。
  • Name, Email, PasswordHash, ...

(3) 分析表 (analytics)

  • AnalyticsID (Partition Key): 唯一分析記錄ID。
  • URLHash: 關(guān)聯(lián)的短鏈碼。
  • ClickTimestamp: 點(diǎn)擊時(shí)間。
  • ReferringSite: 來源網(wǎng)站。
  • Location: 地理位置。

5. 核心算法:如何生成短鏈碼?

面試官:“數(shù)據(jù)庫選型和表結(jié)構(gòu)設(shè)計(jì)都比較合理?,F(xiàn)在我們來聊聊最核心的部分,這個(gè)短鏈碼(比如 vzet59pa)到底該怎么生成呢?”

這是整個(gè)設(shè)計(jì)的靈魂。你可以提出幾種方案,并分析優(yōu)劣,展示你的技術(shù)廣度和深度。

(1) 方案一:哈希編碼

最直接的想法是對原始長URL進(jìn)行哈希,然后取一部分作為短鏈碼。

“一個(gè)直接的思路是對原始URL進(jìn)行哈希,比如用MD5生成一個(gè)128位的哈希值,然后通過Base64編碼將其轉(zhuǎn)換為字符串。編碼方式有很多種,比如base36 (a-z, 0-9) 或 base62 (A-Z, a-z, 0-9),如果加上 + 和 / 就是標(biāo)準(zhǔn)的Base64編碼?!?/p>

標(biāo)準(zhǔn)的Base64編碼表如下:

在使用標(biāo)準(zhǔn)Base64時(shí),我們需要考慮一個(gè)問題:短鏈碼的長度應(yīng)該設(shè)為多少?6位、8位還是10位?

  • 使用Base64編碼,一個(gè)6位長度的短碼,可以產(chǎn)生 64^6 ≈ 687億 個(gè)可能的組合。
  • 使用Base64編碼,一個(gè)8位長度的短碼,可以產(chǎn)生 64^8 ≈ 281萬億 個(gè)可能的組合。

“假設(shè)6位長度的短碼(687億)已經(jīng)足夠滿足我們的系統(tǒng)需求。如果我們使用MD5算法,它會生成一個(gè)128位的哈希值,經(jīng)過Base64編碼后會得到一個(gè)超過21個(gè)字符的字符串。我們只取前6位或8位作為短碼,這可能會導(dǎo)致沖突。為了解決這個(gè)問題,我們可以采取一些策略,比如當(dāng)發(fā)現(xiàn)沖突時(shí),換取哈希值的其他部分,或者對原始URL加鹽后重新哈希?!?/p>

“不過,這個(gè)方案還有幾個(gè)更麻煩的問題。第一,標(biāo)準(zhǔn)Base64中的 + 和 / 字符在URL中是特殊保留字符,其中“+”和“/”在URL中會被編碼為“%2B”以及“%2F”,?“%”在寫?數(shù)據(jù)庫的時(shí)候?和SQL編碼規(guī)則沖突,需要進(jìn)?再編碼,因此直接使?標(biāo)準(zhǔn)Base64編碼進(jìn)?短URL編碼并不合適,直接使用會導(dǎo)致問題。URL保留字符編碼表如下。

“所以,我們需要對Base64編碼進(jìn)行改造,使其對URL友好。一個(gè)常見的做法是將 + 替換為 -,將 / 替換為 _。這樣改造后的字符集就完全可以在URL中安全使用了?!?/p>

“第二,如果多個(gè)用戶輸入同一個(gè)URL,他們會得到相同的短鏈接,這在某些需要獨(dú)立追蹤的場景下是不可接受的。

第三,URL的部分內(nèi)容如果經(jīng)過編碼,例如 .../?id=design 和 .../%3Fid%3Ddesign,本質(zhì)上是同一個(gè)URL,但哈希值卻完全不同。針對這些問題,我們可以為每個(gè)輸入U(xiǎn)RL附加一個(gè)遞增的序列號或者唯一的用戶ID來確保唯一性,不過這個(gè)序列號無需存入數(shù)據(jù)庫。這種方法可能帶來的問題是序列號會無限增長——是否存在溢出風(fēng)險(xiǎn)?同時(shí)附加遞增序列號也會影響服務(wù)性能,引入新的復(fù)雜度?!?/p>

(2) 方案二:自增ID轉(zhuǎn)碼(推薦)

我們可以有一個(gè)獨(dú)立的密鑰生成服務(wù)(KGS),它預(yù)先生成隨機(jī)的六字母字符串并將其存儲在數(shù)據(jù)庫中(我們稱之為 key-DB)。每當(dāng)我們想要縮短一個(gè) URL 時(shí),我們會取一個(gè)已經(jīng)生成的密鑰并使用它。這種方法就簡單了,我們不僅不需要對 URL 進(jìn)行編碼,而且也不必?fù)?dān)心重復(fù)或沖突。KGS 將確保所有插入到 key-DB 中的密鑰都是唯一的。生成六字母字符串的方式這里我們可以用自增ID轉(zhuǎn)碼的方式來實(shí)現(xiàn)

“考慮到哈希方案的種種復(fù)雜性,我更推薦另一種方案:發(fā)號器 + 進(jìn)制轉(zhuǎn)換。我們可以使用一個(gè)全局唯一的ID生成服務(wù)(類似Snowflake算法或數(shù)據(jù)庫自增ID),每當(dāng)有新的短鏈生成請求時(shí),就獲取一個(gè)唯一的十進(jìn)制ID?!?/p>

比如,我們獲取到了ID 10086。然后,我們將這個(gè)ID從十進(jìn)制轉(zhuǎn)換為62進(jìn)制(a-z, A-Z, 0-9)。

10086 (10進(jìn)制) = 2Bi (62進(jìn)制)

這樣,2Bi 就是我們的短鏈碼。這個(gè)方案的優(yōu)點(diǎn)非常突出:

  • 絕對唯一:每個(gè)ID都是唯一的,所以轉(zhuǎn)換后的短鏈碼也絕對不會沖突。
  • 長度可控:生成的短鏈碼長度是遞增的,可以從很短開始,有效利用了碼空間。
  • 性能高效:轉(zhuǎn)換過程是純計(jì)算,速度極快。

面試官:“這個(gè)方案聽起來不錯(cuò)。但這個(gè)全局ID生成器不就成了系統(tǒng)的單點(diǎn)了嗎?如果它掛了,整個(gè)服務(wù)就不可用了?!?/p>

“您提到了關(guān)鍵點(diǎn)。這個(gè)方案通常被稱為密鑰生成服務(wù) (Key Generation Service, KGS)。為了解決單點(diǎn)問題,KGS本身需要設(shè)計(jì)成高可用的集群。我們可以為KGS設(shè)置備用副本,當(dāng)主服務(wù)器宕機(jī)時(shí),備用服務(wù)器就能接管。同時(shí),KGS可以預(yù)先生成大量的唯一短碼,并存入一個(gè)專用的數(shù)據(jù)庫(Key-DB)。為了提升性能和可用性,我們可以設(shè)計(jì)兩張表:一張存未使用的密鑰,一張存已使用的。當(dāng)KGS分配密鑰給應(yīng)用服務(wù)器時(shí),就將其從未用表移動(dòng)到已用表。KGS還可以在內(nèi)存中緩存一批密鑰,以便快速響應(yīng)。為了避免多個(gè)應(yīng)用服務(wù)器同時(shí)請求到同一個(gè)密鑰,KGS在分配密鑰時(shí)需要加鎖同步?!?/p>

“關(guān)于這個(gè)密鑰數(shù)據(jù)庫的大小,我們也可以估算一下。采用62進(jìn)制,6位長度的短碼,大約有 62^6 ≈ 568億 種組合。如果每個(gè)字符占1字節(jié),那么存儲所有密鑰需要 6 * 568億 ≈ 340GB 的空間?!?/p>

我們?nèi)绾芜M(jìn)行密鑰查找?我們可以在數(shù)據(jù)庫中查找該密鑰以獲取完整的 URL。如果該密鑰存在于數(shù)據(jù)庫中,則向?yàn)g覽器返回一個(gè)“HTTP 302 重定向”狀態(tài),并在請求的“Location”字段中傳遞存儲的 URL。如果該密鑰不在我們的系統(tǒng)中,則返回一個(gè)“HTTP 404 未找到”狀態(tài)或?qū)⒂脩糁囟ㄏ蚧刂黜摗?/p>

6. 數(shù)據(jù)分區(qū)與復(fù)制

前面我們估算出有15TB的數(shù)據(jù),單機(jī)數(shù)據(jù)庫肯定扛不住,所以必須進(jìn)行數(shù)據(jù)分區(qū)(Sharding)。

面試官:“具體說說你會怎么分區(qū)?”

主要有兩種思路:

  • 基于范圍的分區(qū):我們可以根據(jù)短鏈碼的首字母來分區(qū)。比如所有以'a'開頭的存一個(gè)分片,'b'開頭的存另一個(gè)。以此類推。這種策略稱為基于范圍的分區(qū)。我們甚至可以將某些出現(xiàn)頻率較低的字母組合存入同一個(gè)數(shù)據(jù)庫分區(qū)。

這種方式實(shí)現(xiàn)簡單,但很容易導(dǎo)致數(shù)據(jù)傾斜和熱點(diǎn)問題。例如,我們決定將所有以字母'E'開頭的 URL 存入某個(gè)數(shù)據(jù)庫分區(qū),但后來發(fā)現(xiàn)以'E'開頭的 URL 數(shù)量過多。

  • 基于哈希的分區(qū):這是更優(yōu)的選擇。我們對短鏈碼(Hash)進(jìn)行哈希計(jì)算,然后對分片總數(shù)取模,決定這條記錄應(yīng)該存儲在哪個(gè)數(shù)據(jù)庫分片上。例如 hash(short_code) % 256。這種方式能讓數(shù)據(jù)均勻分布。

為了更好地處理增刪節(jié)點(diǎn)帶來的數(shù)據(jù)遷移問題,我們還可以引入一致性哈希算法。同時(shí),為了保證數(shù)據(jù)的高可用,每個(gè)數(shù)據(jù)庫分片都應(yīng)該有主從副本,實(shí)現(xiàn)讀寫分離和故障轉(zhuǎn)移。

7. 緩存策略

為了應(yīng)對每秒2萬次的讀取請求,緩存是必不可少的一環(huán)。我們可以在應(yīng)用服務(wù)器和數(shù)據(jù)庫之間加入分布式緩存層,比如Redis或Memcached。

我們可以對高頻訪問的 URL 進(jìn)行緩存。采用 redis 等現(xiàn)成解決方案即可存儲完整 URL 及其對應(yīng)哈希值。這樣應(yīng)用服務(wù)器在訪問后端存儲前,能快速檢查緩存中是否存在目標(biāo) URL。

緩存內(nèi)容:緩存 短鏈碼 -> 原始長URL 的映射關(guān)系。

工作流程:

  • 當(dāng)請求到達(dá)時(shí),應(yīng)用服務(wù)器首先查詢緩存。
  • 如果緩存命中(Cache Hit),直接從緩存中獲取原始URL并返回重定向。
  • 如果緩存未命中(Cache Miss),則查詢后端的數(shù)據(jù)庫。
  • 從數(shù)據(jù)庫查到數(shù)據(jù)后,先將其寫入緩存,然后再返回給用戶。這樣后續(xù)相同的請求就能直接命中緩存了。

面試官:那需要配置多大的緩存內(nèi)存呢?

我們可以從每日流量的 20%開始,根據(jù)客戶端使用模式動(dòng)態(tài)調(diào)整所需緩存服務(wù)器的數(shù)量。根據(jù)前文估算,緩存 20%的日流量需要 170GB 內(nèi)存。由于現(xiàn)代服務(wù)器可配備 256GB 內(nèi)存,我們完全可以用單臺機(jī)器承載全部緩存。當(dāng)然,也可以選擇用多臺配置較低的服務(wù)器來存儲這些熱門 URL。

哪種緩存淘汰策略最適合我們的需求?

可以使用 LRU (Least Recently Used) 策略。因?yàn)闊狳c(diǎn)鏈接會經(jīng)常被訪問,而冷門鏈接則會逐漸被淘汰出緩存,這符合我們的業(yè)務(wù)場景。

同時(shí),為了進(jìn)一步提升效率,我們可以部署緩存集群。當(dāng)發(fā)生緩存未命中并從數(shù)據(jù)庫取回?cái)?shù)據(jù)后,應(yīng)用服務(wù)器需要將這個(gè)新條目寫入所有緩存副本,以保證數(shù)據(jù)一致性。

8. 負(fù)載均衡

十億級別的短鏈存儲,在業(yè)務(wù)上訪問量應(yīng)該不低,所以服務(wù)實(shí)例基本上都要采用多節(jié)點(diǎn)部署。因此我們可以在系統(tǒng)中的三個(gè)位置添加負(fù)載均衡層。

“我們的應(yīng)用服務(wù)、緩存服務(wù)、數(shù)據(jù)庫服務(wù)都會是集群部署,所以在系統(tǒng)的關(guān)鍵節(jié)點(diǎn)都需要部署負(fù)載均衡器(Load Balancer)。”

  • 客戶端 -> 應(yīng)用服務(wù)器
  • 應(yīng)用服務(wù)器 -> 數(shù)據(jù)庫集群
  • 應(yīng)用服務(wù)器 -> 緩存集群

“最初,我們可以采用簡單的輪詢策略。但這種策略不關(guān)心服務(wù)器的實(shí)際負(fù)載。如果某臺服務(wù)器因?yàn)槟承┰蝽憫?yīng)變慢,輪詢策略依然會給它分配新請求,可能導(dǎo)致問題惡化。因此,更智能的方案是采用最少連接或基于響應(yīng)時(shí)間的加權(quán)輪詢等策略,負(fù)載均衡器會定期探測后端服務(wù)器的健康狀況和負(fù)載,動(dòng)態(tài)地調(diào)整流量分配?!?/p>

9.過期鏈接清理

面試官:“設(shè)計(jì)得差不多了。最后一個(gè)問題,那些設(shè)置了過期時(shí)間的鏈接,你們是怎么處理的?會有一個(gè)定時(shí)任務(wù)去掃描數(shù)據(jù)庫刪除嗎?”

這是一個(gè)考察系統(tǒng)維護(hù)和資源優(yōu)化的好問題。

主動(dòng)掃描整個(gè)龐大的數(shù)據(jù)庫去刪除過期鏈接,成本太高,會對數(shù)據(jù)庫造成不必要的壓力。我們可以采用更優(yōu)雅的惰性刪除(Lazy Deletion)和異步清理結(jié)合的策略。

  • 設(shè)置默認(rèn)過期時(shí)間:我們可以為每個(gè)鏈接設(shè)置默認(rèn)有效期(例如兩年)
  • 訪問時(shí)刪除:當(dāng)一個(gè)用戶訪問某個(gè)短鏈接時(shí),我們在查詢到數(shù)據(jù)后,會檢查其是否過期。如果已過期,我們不會返回重定向,而是直接刪除該記錄,并向用戶返回一個(gè)“鏈接已失效”的頁面。
  • 異步清理:同時(shí),我們還會有一個(gè)低優(yōu)先級的后臺清理服務(wù),它會在系統(tǒng)負(fù)載較低的時(shí)候(比如凌晨),慢慢地、分批地去清理那些已經(jīng)過期但長時(shí)間未被訪問的“僵尸”鏈接。這個(gè)服務(wù)必須被設(shè)計(jì)成輕量級的,避免影響核心業(yè)務(wù)。
  • 密鑰復(fù)用:在刪除了過期鏈接后,我們可以將它對應(yīng)的短鏈碼回收,放回到KGS的未使用密鑰庫中,實(shí)現(xiàn)循環(huán)利用。

四、小結(jié)

至此,一個(gè)相對完整、考慮周全的十億級短鏈系統(tǒng)設(shè)計(jì)方案就展現(xiàn)在面試官面前了。我們再來回顧一下整體架構(gòu)。

從一個(gè)簡單的需求出發(fā),我們通過需求分析、容量預(yù)估,明確了系統(tǒng)的核心挑戰(zhàn)。然后,我們定義了清晰的API接口,設(shè)計(jì)了可擴(kuò)展的數(shù)據(jù)庫模型,并深入探討了最核心的短碼生成算法。為了應(yīng)對高性能和高可用的要求,我們引入了數(shù)據(jù)分區(qū)、緩存和負(fù)載均衡等關(guān)鍵組件。最后,我們還考慮到了過期數(shù)據(jù)清理這樣的維護(hù)性細(xì)節(jié)。

整個(gè)回答過程,不僅展示了你的技術(shù)深度和廣度,更重要的是,體現(xiàn)了你作為一名架構(gòu)師,那種從全局出發(fā)、層層遞-進(jìn)、權(quán)衡利弊的系統(tǒng)化思維方式。這才是面試官最想看到的。

責(zé)任編輯:趙寧寧 來源: IT楊秀才
相關(guān)推薦

2025-06-04 03:15:00

高并發(fā)短鏈系統(tǒng)

2022-09-13 08:01:58

短鏈服務(wù)哈希算法字符串

2025-10-20 04:00:00

2025-09-17 10:08:43

2025-09-19 09:57:46

2021-05-19 08:17:35

秒殺場景高并發(fā)

2025-09-28 01:50:00

2022-09-13 17:45:40

長網(wǎng)址短鏈系統(tǒng)

2018-09-10 15:14:27

前端WebURL

2025-04-29 02:00:00

高并發(fā)系統(tǒng)場景

2023-01-18 17:50:35

系統(tǒng)架構(gòu)Kafka

2023-07-26 13:29:43

高性能短鏈系統(tǒng)

2024-04-09 08:39:16

本地緩存開發(fā)線程安全

2024-10-07 08:52:59

分布式系統(tǒng)分布式 IDID

2024-10-29 08:17:43

2024-12-26 10:19:16

2021-06-09 07:55:19

NodeEventEmitte驅(qū)動(dòng)

2024-11-12 08:13:09

2024-11-19 16:31:23

2024-08-07 08:15:47

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號