系統設計面試:如何設計一個1 億日活 + 10 萬 QPS 短鏈系統?
發微博時,長鏈接占了 100 多字符,剩下的內容根本寫不下;分享大眾點評的餐廳鏈接,太長導致朋友復制時總出錯;電商運營想自定義 “http://t.cn/618sale” 這樣的短鏈,還希望 24 小時后自動失效 —— 這些場景下,短鏈服務成了 “剛需”。
但你有沒有想過:用戶自定義的短鏈重復了該怎么辦?10 萬 QPS 的訪問量下,怎么保證用戶點短鏈后瞬間跳轉?過期短鏈不清理,數據庫會不會被撐爆?
今天我們結合 TinyURL、bit.ly 的設計邏輯,從需求到落地,一步步拆解短鏈系統的核心模塊,把 KGS 密鑰生成、自定義短鏈、惰性清理、權限控制等關鍵細節講透,帶你避開那些 “踩過才知道的坑”。
一、需求與預算:從 TinyURL 場景看 “短鏈系統該滿足什么”
設計短鏈系統前,必須先明確 “功能邊界” 和 “資源成本”—— 既要覆蓋 TinyURL 這類工具的核心場景,又要避免過度設計。我們以 “支撐每月 5 億新短鏈、100:1 讀寫比” 的標準,拆解需求與預算。
1.1 功能需求:從 “基礎生成” 到 “自定義與權限”
短鏈系統的核心是 “生成” 與 “訪問”,但 TinyURL 等服務的實際場景中,還需要支持自定義、過期控制、權限管理等進階功能,結合案例更易理解:
功能點 | 業務場景案例(TinyURL / 電商) | 設計關鍵 |
長鏈→唯一短鏈 | 用戶提交 “https://www.educative.io/collection/page/5668639101419520”,系統生成 “http://tinyurl.com/jlg8zpc” | 長鏈加唯一索引,避免重復生成 |
自定義短鏈 | 電商運營想將 “https://taobao.com/618/sale” 自定義為 “http://t.cn/618sale”,方便用戶記憶 | 自定義別名加唯一索引,校驗沖突后生成 |
過期時間控制 | 運營設置 “618sale” 短鏈 24 小時后失效,避免用戶訪問過期活動頁 | 表中增加expire_date字段,訪問時校驗 |
短鏈刪除 | 某用戶誤生成違規短鏈,通過deleteURL接口刪除,禁止訪問 | 軟刪除(deleted_at標記),避免數據碎片 |
權限控制 | 企業用戶創建 “私有短鏈”,僅指定員工能訪問(如 “http://t.cn/team-report”) | 新增權限表,關聯短鏈與有權限的用戶 ID |
1.2 非功能需求:TinyURL 級別的高可用與低延遲
短鏈服務是 “用戶直接感知” 的服務 —— 如果用戶點短鏈后 3 秒沒跳轉,大概率會放棄訪問。參考 TinyURL 的設計標準,非功能需求必須嚴格達標:
- 高可用:可用性需達 99.99%(每年 downtime 不超過 52 分鐘),單節點宕機不影響服務,比如北京節點掛了,上海節點能繼續處理請求;
- 高性能:支撐 20K QPS 讀請求(用戶訪問短鏈)和 200 QPS 寫請求(生成短鏈),讀延遲≤50ms,寫延遲≤100ms;
- 不可猜測性:短鏈不能被輕易破解(如 “http://t.cn/123456” 這類有序 ID 易被遍歷),需用隨機生成的密鑰(如 Base64 編碼的 6 位字符)。
1.3 容量估算:5 年 15TB 存儲,170GB 緩存
基于 “每月 5 億新短鏈、讀寫比 100:1” 的假設,我們可以精準計算資源需求,避免 “拍腦袋” 設計(數據參考 TinyURL 場景):
- 存儲需求:每條短鏈 - 長鏈映射占 500Bytes,每月 5 億條需 25GB(5 億 ×500Bytes=25×10?Bytes),5 年就是 15TB(25GB×12×5),NoSQL 數據庫(如 Cassandra)或 MySQL 分庫分表均可承載;
- 緩存需求:遵循 “80-20 規則”——20% 的熱點短鏈貢獻 80% 的訪問量。每天 20K QPS×3600×24=17 億次訪問,緩存 20% 需 170GB 內存(0.2×17 億 ×500Bytes≈170GB),用 Redis + 本地緩存的多級架構;
- 帶寬需求:寫請求(生成短鏈)每秒傳入 100KB(200×500Bytes),讀請求(訪問短鏈)每秒傳出 10MB(20K×500Bytes),普通云服務器帶寬完全能覆蓋;
- 內存需求:除緩存外,KGS(密鑰生成服務)預存 6 位 Base64 密鑰需 412GB(68.7 億條 ×6Bytes / 條),可分多臺服務器存儲。
二、核心時序:短鏈 “生成”“訪問”“刪除” 的完整流程
短鏈系統的核心是 “寫流程”(生成短鏈)、“讀流程”(訪問短鏈)和 “刪流程”(刪除短鏈),這三個流程決定了系統的性能和可靠性。我們結合 TinyURL 的 API 設計,拆解每一步的邏輯。
2.1 寫流程:支持自定義短鏈,怎么避免沖突?
用戶調用 createURL(api_dev_key, original_url, custom_alias="618sale", expire_date="2024-06-20") 接口,生成短鏈的步驟如下:
1.API 密鑰配額校驗:先校驗 api_dev_key 的配額(比如每個 key 每天最多生成 1000 條短鏈),防止惡意刷請求。
- 案例:某惡意用戶的
api_dev_key已生成 1000 條短鏈,系統返回 “配額不足” 錯誤,拒絕新請求;
2.自定義別名沖突校驗:若用戶傳入 custom_alias="618sale",先查數據庫的 custom_alias 唯一索引 —— 若已存在(比如其他電商已用),返回 “別名已被占用”,并推薦 “618sale2”“618sale_new” 等可用別名;
3.生成短鏈密鑰:
- 若有自定義別名且無沖突,直接用別名作為短鏈密鑰(如 “618sale”);
- 若無自定義別名,從 KGS(密鑰生成服務)獲取預生成的 6 位 Base64 密鑰(如 “jlg8zpc”);
4.寫入數據庫:將 “長鏈、短鏈密鑰、自定義別名、過期時間、創建者 ID、權限類型” 等信息寫入tiny_urls表和url_permissions表(權限表);
5.緩存預熱:同步將 “短鏈密鑰→長鏈” 映射寫入本地緩存(過期時間 1 小時)和 Redis(過期時間隨機 1-2 小時,避免緩存雪崩)。
2.2 讀流程:如何實現 “50ms 內跳轉” 并校驗權限?
用戶點擊短鏈 “http://t.cn/618sale”,系統的處理步驟如下:
- 短鏈解析:提取短鏈密鑰 “618sale”(若為默認生成的密鑰,無需解碼;若為 ID 編碼,需 Base64 解碼);
- 本地緩存查詢:應用服務器先查本地緩存(如 Caffeine),若存在 “618sale→https://taobao.com/618/sale” 的映射,直接進入權限校驗步驟,延遲≤10ms;
- 分布式緩存查詢:若本地緩存未命中,查 Redis(Key 為 “short:618sale”),若存在則返回長鏈,并同步更新本地緩存,延遲≤30ms;
- 數據庫查詢與校驗:若 Redis 未命中,查tiny_urls表(通過short_key或custom_alias索引),同時校驗:
過期時間:若expire_date<當前時間,返回 404,并觸發 “惰性清理”(刪除該短鏈,回收密鑰);
軟刪除:若deleted_at不為 null,返回 404;
權限:若為私有短鏈,查url_permissions表,判斷當前用戶是否有權限(如用戶 ID 在 “允許訪問列表” 中),無權限則返回 401;
- 302 重定向:校驗通過后,用 302 臨時重定向(而非 301 永久重定向)——302 能統計訪問量,還能靈活修改短鏈指向;
- 異步統計:發送 “訪問日志”(用戶位置、設備、時間)到 Kafka,后續由離線任務寫入時序數據庫(如 InfluxDB),用于分析活動效果。
2.3 刪流程:如何安全刪除短鏈并回收密鑰?
用戶調用 deleteURL(api_dev_key, url_key="618sale") 接口,刪除短鏈的步驟如下:
- 權限校驗:校驗
api_dev_key對應的用戶是否為短鏈的創建者(或管理員),無權限則返回 403; - 軟刪除標記:更新tiny_urls表的deleted_at字段為當前時間(不物理刪除,便于后續恢復);
- 緩存清理:
刪除本地緩存中 “618sale” 的映射;
發送 Redis Pub/Sub 消息,通知所有應用節點刪除本地緩存;
刪除 Redis 中 “short:618sale” 的鍵;
- 密鑰回收:若短鏈已過期(或刪除時已超過有效期),將密鑰 “618sale” 放回 KGS 的 “未使用密鑰庫”,供后續復用;若未過期,暫存到 “待回收密鑰庫”,過期后再復用。
三、核心模塊深度設計:從 TinyURL 邏輯避坑
短鏈系統的穩定性,取決于 KGS 密鑰生成、數據庫設計、多級緩存、惰性清理、統計與權限這五大核心模塊。每個模塊都有 “坑”,參考 TinyURL 的設計邏輯,我們逐一拆解解決方案。
3.1 KGS 密鑰生成服務:怎么預生成 “不可猜測” 的短鏈?
TinyURL 的短鏈是 6 位隨機字符(如 “jlg8zpc”),而非有序 ID,這既保證不可猜測性,又避免哈希沖突。核心解決方案是KGS(密鑰生成服務) —— 預生成大量隨機密鑰,按需分配,徹底解決 “實時生成密鑰的沖突問題”。
1. KGS 的核心設計:預生成 + 雙庫存儲
- 密鑰生成規則:用 Base64 編碼(包含 a-z、A-Z、0-9,共 62 個字符)生成 6 位密鑰,可產生 62?≈568 億條唯一密鑰,足夠支撐多年需求;
- 雙庫存儲:KGS 維護兩個數據庫表:
unused_keys:存儲未使用的密鑰(如 “jlg8zpc”“24rgcX”);
used_keys:存儲已分配的密鑰(關聯短鏈 ID,避免重復);
- 預生成策略:KGS 后臺線程每秒生成 1000 條密鑰,插入unused_keys表,確保庫中有足夠的 “庫存”(如保持 100 萬條未使用密鑰);
- 密鑰分配流程:應用服務器需要密鑰時,向 KGS 發送請求,KGS 從unused_keys表中取 100 條密鑰(批量取,減少 DB 交互),標記為 “已分配” 并移動到used_keys表,再返回給應用服務器;應用服務器本地緩存這些密鑰,按需使用。
2. KGS 的并發與容錯(避坑關鍵)
- 并發沖突解決:KGS 取密鑰時用 “行級鎖”(如 MySQL 的SELECT ... FOR UPDATE),確保同一密鑰不會被多個應用服務器獲取。
案例:應用服務器 A 和 B 同時請求密鑰,KGS 對unused_keys表的前 100 條記錄加鎖,A 取走后,B 只能取接下來的 100 條,避免沖突;
- KGS 單點故障:部署 KGS 主從架構,主 KGS 負責生成和分配密鑰,從 KGS 實時同步unused_keys和used_keys表;若主 KGS 宕機,從 KGS 在 10 秒內切換為主節點,繼續提供服務;
- 密鑰浪費處理:若應用服務器獲取 100 條密鑰后宕機,未使用的密鑰會留在used_keys表中 ——KGS 每天凌晨運行 “密鑰回收任務”,將 “分配后 24 小時未關聯短鏈” 的密鑰移回unused_keys表,減少浪費。
3. 代碼片段:KGS 密鑰分配接口(Go)
// KGS服務:批量獲取未使用的密鑰
func (s *KGSService) GetUnusedKeys(ctx context.Context, count int) ([]string, error) {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}()
// 1. 加行級鎖,獲取count條未使用的密鑰
rows, err := tx.QueryContext(ctx,
"SELECT key FROM unused_keys ORDER BY id LIMIT ? FOR UPDATE", count)
if err != nil {
return nil, err
}
defer rows.Close()
var keys []string
for rows.Next() {
var key string
if err := rows.Scan(&key); err != nil {
return nil, err
}
keys = append(keys, key)
}
// 2. 將獲取的密鑰移到used_keys表(標記為已分配)
if len(keys) > 0 {
placeholders := strings.Repeat("?,", len(keys))[:len(keys)*2-1]
_, err = tx.ExecContext(ctx,
fmt.Sprintf("INSERT INTO used_keys (key, allocated_at) VALUES (%s)", placeholders),
func() []interface{} {
args := make([]interface{}, len(keys)*2)
for i, k := range keys {
args[2*i] = k
args[2*i+1] = time.Now()
}
return args
}()...)
if err != nil {
return nil, err
}
// 3. 從unused_keys表刪除已分配的密鑰
_, err = tx.ExecContext(ctx,
fmt.Sprintf("DELETE FROM unused_keys WHERE key IN (%s)", placeholders),
func() []interface{} {
args := make([]interface{}, len(keys))
for i, k := range keys {
args[i] = k
}
return args
}()...)
if err != nil {
return nil, err
}
}
return keys, nil
}3.2 數據庫設計:從 TinyURL 看 “多表關聯與分庫分表”
短鏈系統需要存儲 “短鏈映射”“用戶信息”“權限控制” 三類數據,參考 TinyURL 的 NoSQL 選擇(也支持 MySQL),我們設計多表結構,平衡性能與擴展性。
1. 核心表結構(MySQL/NoSQL 通用邏輯)
表 1:tiny_urls(短鏈映射主表)
create table tiny_urls
(
id bigint unsigned auto_increment -- 自增主鍵,順序寫入
primary key,
created_at datetime(3) not null, -- 創建時間(毫秒級)
updated_at datetime(3) not null, -- 更新時間
deleted_at datetime(3) null, -- 軟刪除標記(null=未刪除)
expire_date datetime(3) null, -- 過期時間(null=永久)
long_url varchar(500) not null, -- 原始長鏈
short_key varchar(20) not null, -- 短鏈密鑰(默認6位Base64)
custom_alias varchar(20) null, -- 自定義別名(可為null)
user_id bigint not null, -- 創建者ID(關聯users表)
permission tinyint not null default 0,-- 權限類型:0=公開,1=私有
visit_count bigint default 0, -- 訪問次數(異步更新)
constraint uk_tiny_urls_long_url
unique (long_url), -- 長鏈唯一,避免重復生成
constraint uk_tiny_urls_short_key
unique (short_key), -- 短鏈密鑰唯一
constraint uk_tiny_urls_custom_alias
unique (custom_alias) -- 自定義別名唯一(不為null時)
);
-- 索引優化:加速“未刪除+未過期”的查詢
create index idx_tiny_urls_deleted_expire
on tiny_urls (deleted_at, expire_date);表 2:url_permissions(短鏈權限表,私有短鏈用)
create table url_permissions
(
id bigint unsigned auto_increment
primary key,
short_key varchar(20) not null, -- 短鏈密鑰(關聯tiny_urls)
user_id bigint not null, -- 有權限的用戶ID
created_at datetime(3) not null,
constraint uk_url_permissions_short_user
unique (short_key, user_id) -- 避免重復授權
);
-- 索引:加速“短鏈→有權限用戶”的查詢
create index idx_url_permissions_short_key
on url_permissions (short_key);表 3:users(用戶表,存儲 API 密鑰與配額)
create table users
(
id bigint unsigned auto_increment
primary key,
created_at datetime(3) not null,
api_dev_key varchar(50) not null, -- API密鑰(唯一)
daily_quota int not null default 1000, -- 每日生成配額
used_quota int not null default 0, -- 今日已用配額
last_reset_time date not null, -- 配額重置時間
constraint uk_users_api_dev_key
unique (api_dev_key)
);2. 分庫分表設計(支撐 15TB 存儲)
當數據量達 10 億條時,單庫單表性能會急劇下降,參考 TinyURL 的分區邏輯,我們采用 “基于短鏈密鑰哈希的分庫分表”:
- 分庫策略:對short_key做哈希,取模 16(分成 16 個庫),如hash(short_key) %16=0→庫 0,%16=1→庫 1;
- 分表策略:每個庫內按user_id取模 32(分成 32 張表),如user_id%32=0→表 0,%32=1→表 1;
- 效果:16 個庫 ×32 張表 = 512 張表,每張表存儲約 2 億條數據(300 億總數據 ÷512),MySQL 單表性能可支撐;
- 路由邏輯:應用服務器通過 “短鏈密鑰→庫”“用戶 ID→表” 的路由規則,快速定位數據所在的庫表。
3.3 過期短鏈處理:惰性清理 + 定時回收,避免 DB 壓力
如果主動遍歷所有短鏈清理過期數據,會給數據庫帶來巨大壓力。參考 TinyURL 的 “惰性清理” 邏輯,我們采用 “訪問時清理 + 定時批量清理” 的組合方案,平衡性能與數據有效性。
1. 惰性清理(用戶訪問時觸發)
- 場景:用戶訪問已過期的短鏈 “http://t.cn/618sale”(expire_date=2024-06-20,當前時間 2024-06-21);
- 處理步驟:
數據庫查詢時發現expire_date<當前時間,返回 “短鏈已過期”(404);
異步發送 “清理任務” 到消息隊列,后臺線程更新tiny_urls表的deleted_at字段(標記為刪除);
將短鏈密鑰 “618sale” 移回 KGS 的unused_keys表,供后續復用;
- 優點:只清理 “被訪問的過期短鏈”,無需遍歷全表,DB 壓力小。
2. 定時批量清理(凌晨低峰期執行)
- 場景:大量過期短鏈長期未被訪問(如 “618sale” 過期后無人訪問),占用 DB 空間;
- 處理步驟:
每天凌晨 2 點,啟動定時任務,查詢tiny_urls表中 “expire_date<當前時間且deleted_at=null” 的短鏈(每次查 1000 條,避免鎖表);
批量更新deleted_at字段,標記為刪除;
批量回收密鑰到 KGS 的unused_keys表;
- 優化:用 “分頁查詢 + 多線程” 處理,每次只處理 1 萬條,避免影響正常業務。
3. 案例:清理效果對比
清理方案 | 處理 100 萬條過期短鏈的時間 | DB CPU 使用率 | 對正常業務影響 |
主動遍歷全表 | 2 小時 | 90%(峰值) | 正常查詢延遲從 50ms 升到 200ms |
惰性 + 定時清理 | 1 小時(分散在 24 小時 + 凌晨 1 小時) | 30%(峰值) | 無明顯影響 |
3.4 統計模塊:TinyURL 級別的訪問分析,怎么實現?
短鏈系統需要統計 “訪問次數、用戶位置、設備信息” 等數據,用于分析活動效果(如電商 618 短鏈的訪問量)。如果每次訪問都同步更新 DB,會導致性能瓶頸,我們采用 “異步收集 + 時序數據庫存儲” 的方案。
1. 統計數據收集流程
- 訪問日志生成:用戶訪問短鏈時,應用服務器生成訪問日志(JSON 格式),包含:
{
"short_key": "618sale",
"visit_time": "2024-06-18T12:00:00.123Z",
"user_ip": "114.247.50.100",
"location": "北京", // 通過IP解析
"device": "iPhone 15", // 通過User-Agent解析
"referrer": "https://weibo.com" // 來源頁面
}- 異步發送日志:應用服務器將日志發送到 Kafka(主題short_url_visit_log),無需等待響應,不阻塞重定向流程;
- 日志消費與存儲:Flink 任務消費 Kafka 日志,做數據清洗(如 IP 解析為地理位置),然后寫入時序數據庫(如 InfluxDB)—— 時序數據庫適合存儲 “時間序列數據”,查詢訪問量趨勢的性能比 MySQL 高 10 倍;
- 訪問次數更新:Flink 任務每小時批量統計每個短鏈的訪問次數,更新tiny_urls表的visit_count字段,避免實時更新的 DB 壓力。
2. 統計查詢示例(分析 618 活動效果)
運營人員通過 API 查詢 “618sale” 短鏈的統計數據:
GET /api/statistics?short_key=618sale&start_time=2024-06-18&end_time=2024-06-20返回結果(JSON):
{
"short_key": "618sale",
"total_visits": 125000, // 總訪問量
"daily_visits": [
{"date": "2024-06-18", "count": 80000},
{"date": "2024-06-19", "count": 40000},
{"date": "2024-06-20", "count": 5000}
],
"location_distribution": [
{"location": "北京", "count": 30000},
{"location": "上海", "count": 25000}
],
"device_distribution": [
{"device": "iPhone", "count": 60000},
{"device": "Android", "count": 55000}
]
}3.5 安全與權限:私有短鏈怎么控制訪問?
企業用戶需要 “私有短鏈” 功能(如僅團隊成員能訪問的報告鏈接),參考 TinyURL 的權限設計,我們通過 “權限表 + 訪問校驗” 實現,確保數據安全。
1. 權限控制流程
- 創建私有短鏈:用戶調用createURL接口時,指定permission=1(私有),并傳入 “允許訪問的用戶 ID 列表”(如 [1001,1002,1003]);
系統在tiny_urls表中標記permission=1;
在url_permissions表中插入 “短鏈密鑰→用戶 ID” 的關聯記錄(如 “618sale→1001”“618sale→1002”);
- 訪問私有短鏈:用戶訪問 “http://t.cn/618sale” 時,系統校驗:
- 若用戶未登錄,返回 401(未授權);
- 若用戶已登錄,查詢url_permissions表,判斷 “當前用戶 ID 是否在允許訪問列表中”;
- 若在列表中,允許重定向;若不在,返回 403(無權限)。
2. 案例:企業團隊私有短鏈的訪問控制
- 場景:某公司員工 A 創建私有短鏈 “http://t.cn/team-report”(permission=1),允許員工 B(ID=1002)和 C(ID=1003)訪問;
- 校驗邏輯:
員工 B 訪問時,url_permissions表存在 “team-report→1002” 記錄,允許訪問;
外部用戶 D 訪問時,未登錄,返回 401;
員工 E(ID=1004)登錄后訪問,url_permissions表無對應記錄,返回 403。
四、部署與性能:從 TinyURL 到生產環境的落地
設計再好,落地時也會遇到 “環境差異”“資源不足” 等問題。我們參考 TinyURL 的高可用架構,結合 K8s 部署,確保系統穩定運行。
4.1 部署架構:無狀態服務 + 高可用存儲
短鏈服務是 “無狀態” 的(應用節點不存數據),可以通過 K8s 快速擴容,部署架構如下:
- 接入層:CDN+API 網關,CDN 緩存靜態資源(如短鏈跳轉頁面),API 網關負責路由、配額校驗、HTTPS 卸載;
- 應用層:分為 “讀寫服務” 和 “只讀服務”:
讀寫服務:3 個節點(處理生成、刪除短鏈),暴露 8080 端口;
只讀服務:10 個節點(只處理訪問短鏈),暴露 80 端口,通過 LB 分流讀請求;
- 存儲層:
數據庫:MySQL 主從架構(1 主 2 從),主庫處理寫請求,從庫處理讀請求,支持主從切換;NoSQL(Cassandra)集群,存儲海量短鏈數據;
Redis:3 主 3 從集群,存儲分布式緩存;KGS 專用 Redis,存儲預生成的密鑰;
- 中間件:
Kafka:3 個節點,收集訪問日志和清理任務;
Flink:處理日志統計,寫入時序數據庫;
Prometheus+Grafana:監控 QPS、延遲、緩存命中率等指標。
4.2 性能壓測:TinyURL 級別的性能表現
我們在 “4 核 8G 服務器 ×10 臺” 的環境下做壓測,模擬 TinyURL 的生產場景,結果如下:
1. 寫性能(生成短鏈)
- 測試條件:100 個并發協程,提交 10 萬條不同長鏈(含 20% 自定義別名);
- 結果:平均 QPS 5200,平均延遲 85ms,p99 延遲 120ms;
- 瓶頸:MySQL 主庫寫 QPS 達 5200,CPU 使用率 60%,未達瓶頸(可通過分庫分表進一步提升)。
2. 讀性能(訪問短鏈)
- 測試條件:1000 個并發連接,訪問 10 萬次熱點短鏈(本地緩存命中率 99%);
- 結果:平均 QPS 48000,平均延遲 2.1ms,p99 延遲 4.6ms;
- 瓶頸:應用節點 CPU 使用率 70%,Redis QPS 5000(僅 10% 請求查 Redis),性能冗余充足。
3. 容錯測試(節點宕機)
測試場景:只讀服務宕機 2 個節點,Redis 主節點宕機 1 個,KGS 主節點宕機;
結果:
- LB 自動將請求轉發到其他只讀節點,無請求丟失;
- Redis 從節點 10 秒內切換為主節點,緩存服務無中斷;
- KGS 從節點切換為主節點,密鑰分配正常,短鏈生成無影響;
- 整體訪問延遲從 2.1ms 升到 3.5ms,仍在可接受范圍。
五、總結:短鏈系統的核心設計思路(從 TinyURL 到生產)
短鏈系統看似簡單,實則是 “分布式系統設計” 的縮影 —— 從 TinyURL 的基礎邏輯,到生產環境的高可用落地,每一步都要平衡 “性能”“可靠性” 和 “成本”。核心思路可以總結為四點:
1.密鑰生成:預生成優于實時計算
用 KGS 預生成隨機密鑰,徹底解決哈希沖突和不可猜測性問題,同時通過主從架構避免單點故障,參考 TinyURL 的 6 位 Base64 設計,兼顧短鏈長度與唯一性。
2.數據處理:惰性優于主動
過期短鏈用 “訪問時清理 + 定時批量清理”,避免遍歷全表;訪問統計用 “異步收集 + 批量更新”,避免實時寫 DB 的壓力,這是 TinyURL 能支撐高并發的關鍵。
3.緩存架構:多級優于單一
本地緩存扛熱點 Key,Redis 扛全局緩存,緩存命中率達 99% 以上,將數據庫壓力降到最低;同時用隨機過期時間和分布式鎖,解決緩存雪崩、擊穿問題。
4.權限與統計:按需設計,不過度
私有短鏈用 “權限表 + 訪問校驗”,滿足企業需求;統計功能用 “時序數據庫 + 異步處理”,兼顧分析需求與性能,避免為 “小眾功能” 犧牲核心性能。
現在,當你再用 TinyURL 或大眾點評的短鏈時,應該能想到背后的 “KGS 密鑰分配→多級緩存查詢→302 跳轉→異步統計” 流程了。如果面試官問你 “怎么設計一個類似 TinyURL 的短鏈系統”,你也能從需求、模塊、部署三個維度,清晰地講出完整方案,甚至能指出 “惰性清理”“KGS 容錯” 等細節,展現你的技術深度。



































