題幹與適用場景
設計一個根據已輸入前綴回傳 Top 10 查詢建議的後端服務。假設每日活躍使用者 5,000 萬,每位使用者每天搜尋 10 次,每次搜尋觸發 5 次建議請求,尖峰為每秒 15 萬次請求,p99 延遲目標為 50 毫秒,熱門度更新目標為 15 分鐘,政策下架目標為 1 分鐘。
這些數字都是面試假設,並非正式環境實測。基礎方案提供匿名、依語言地區區分的熱門查詢建議;瀏覽器元件、拼字修正、語意補全與個人排序暫不納入。紀錄中出現過的罕見或違規查詢不能直接成為建議。
這道題的核心跨越事件收集、排序、不可變索引建置、線上服務、快取與分片、內容安全及版本發布,因此歸為 system-design。前端自動完成元件只是此 API 的消費者,Trie 也只是本機索引的一種實作。
面試官考察點
第一項訊號是能否拆開寫入密集的學習鏈路與讀取密集的服務鏈路。每次按鍵都掃描紀錄或即時排序,無法穩定滿足尾端延遲。聚合、准入、審核及主要排序應預先完成,線上鏈路只做有界前綴查詢。
第二項是容量推導。每日請求量為 5,000 萬乘 10 再乘 5,即 25 億;平均約每秒 2.89 萬次,題設尖峰約為平均的 5 倍。若回應估為 1 KB,尖峰回應負載約 150 MB/s,尚未計入協定與副本開銷。這些數字用來決定副本、快取與壓測目標;索引記憶體必須用真實序列化樣本測量,不能憑空假設 Trie 節點大小。
第三項是發布正確性。半成品索引或路由到不同版本會造成缺項與排序漂移。成熟方案建置有版本的不可變產物,驗證後與舊版並存載入,原子切換路由,並保留上一個健康版本回復。
最後,熱門不代表可發布。搜尋紀錄可能包含隱私、操弄與有害文字。不同使用者數門檻、保留規則、反濫用、發布前審核及更快的緊急停用通道都屬於正確性。
回答前需要澄清的問題
- 建議代表什麼? 查詢句、商品實體與導覽入口的資料來源及排序特徵不同;基礎方案回傳完整查詢句。
- 需要哪種比對? 純前綴可用緊湊索引;中綴、模糊或語意比對需要額外召回器,並增加線上預算。
- 不同變更要求多新? 熱門度允許 15 分鐘,政策下架要求 1 分鐘,因此採用版本化基礎索引與獨立停用層。
- 全域還是個人化? 全域結果便於共享快取;個人化會引入授權、刪除與特徵延遲,基礎方案先不做。
- 語言與正規化如何定義? 大小寫、文字系統與重音規則依語言而異,建置端與查詢端必須使用同一版本策略。
- 空前綴與單一字元怎麼辦? 它們極熱且候選寬廣;空前綴回傳編輯選定清單,單字元回傳獨立預先計算清單。
- 安全與隱私要求是什麼? 答案會改變紀錄保留、聚合門檻、審核流程及區域儲存方式。
30 秒回答框架
「我會把系統拆成離線建置與有界線上查詢。搜尋事件進入串流,依語言和時間視窗正規化、聚合,再通過頻率、反濫用、隱私及內容審核。排序工作把候選寫成帶版本的前綴索引,驗證後由服務副本並行載入,再原子切換。線上請求先正規化,查熱門前綴快取,路由至語言及前綴分片,套用快速停用層後回傳十筆。我會按每秒 15 萬尖峰規劃副本,按需拆分熱門前綴,保留舊索引回復,並觀察 p99、覆蓋率、安全召回、舊版本比例及排序品質。」
分步驟深入解答
第一步:推導預算與介面契約
使用小型冪等讀取介面:
GET /v1/suggestions?prefix=iph&locale=zh-TW&limit=10
200 {
"suggestions": [
{ "text": "iphone 充電器", "id": "q_7f2" }
],
"indexVersion": "2026-07-19T17:30Z"
}服務限制 limit 與正規化前綴長度,拒絕未知語言地區,也不允許客戶端傳入排序權重。穩定的不透明 ID 便於分析,且不會把顯示文字當主鍵;indexVersion 讓舊版或混合版本回應可觀測。
25 億日請求對應平均約 2.89 萬 QPS,容量按 15 萬尖峰設計。回應若約 1 KB,尖峰約 150 MB/s,需要區域副本與壓縮傳輸。快取與索引 RAM 則應真正序列化代表性資料,測量每個前綴和候選的位元組數,再加入副本與餘量。
第二步:從紀錄產生可發布候選
客戶端在搜尋完成後傳送查詢 ID、語言地區、粗粒度情境、時間、結果訊號,以及僅用於有界聚合的短期隱私保護主體鍵。收集服務驗證結構、排除明顯機器人,再寫入追加式事件流。視窗聚合計算不同主體數與品質訊號,使用者原始識別碼絕不進入建議主鍵。
候選必須通過不同使用者數下限、限速與反濫用、隱私及保留規則、內容政策分類,再依熱門度、時間衰減、結果品質和編輯規則組合排序。精確權重需透過實驗決定,並非通用常數。拒絕原因進入受限稽核儲存,不進入服務索引。
點擊訊號會受目前顯示位置影響,單看點擊率容易強化既有排序。離線相關性判斷與有護欄的實驗應和互動指標結合,同時觀察覆蓋率、無結果率、申訴與曝光集中度。自動完成源自紀錄和文件表示時可能重現偏見或有害內容,因此審核發生在建置前。
第三步:具體化有界前綴索引
每筆合格建議依線上端相同的語言正規化產生前綴。每個前綴只保存有界候選,例如 API 回傳 10 筆時保存前 20 筆,給去重和緊急過濾留餘量,查詢仍保持有界。
Trie 或有限狀態轉換器能共享前綴;以「語言地區加前綴」為鍵的有序鍵值表較容易維運,也可能有良好壓縮。用產物大小、建置時間、查詢 p99 與更新流程的基準測試選擇。Elasticsearch completion suggester 呈現相同權衡:快速前綴查詢仰賴建置成本較高的記憶體結構,並以權重和情境影響排序與過濾。
基礎範圍只做精確前綴。模糊補全是獨立召回器,因為編輯距離擴展會改變召回、CPU 與安全分析,不能預設共用相同延遲承諾。
第四步:安全發布不可變版本
每次建置記錄輸入水位、正規化版本、排序器版本、政策版本、校驗和及建立時間。驗證包含結構、候選數量、禁止測試詞、語言隔離、穩定同分規則、抽樣查詢、產物大小及載入後延遲。
副本在目前版本旁載入新版,回報校驗和與代表性查詢健康度;控制面再按分片群組原子切換。一個請求固定使用同一版本,混合版本比例另外監控。新版通過小流量觀察前保留舊版;若延遲、安全或覆蓋退化,直接回切路由。
建置失敗或延遲時繼續服務上一個健康版本。新鮮度是 SLO,不能成為發布無效產物的理由。
第五步:縮短線上鏈路
請求依序經過限流、正規化、語言解析與精確熱門前綴快取,再由路由器選擇前綴分片及副本。副本做一次索引查詢,移除快速停用集合的項目,按穩定 ID 去重並取前 10。線上鏈路不存取原始紀錄、不做分散式聚合,也不做完整排序。
快取鍵包含語言地區、正規化前綴、數量上限、政策版本與目前索引版本,避免切換後繼續回傳舊排序或已停用內容。空前綴與單一字元獨立預先計算。不存在的前綴可短期負快取,但 TTL 不能讓新候選超過 15 分鐘仍不可見。
第六步:按局部性與熱門前綴分片
先按語言地區,再按前幾個正規化字元的範圍或雜湊分片。範圍分片有局部性但容易過熱;純雜湊較均衡,卻需要更多路由中繼資料。實用方案維護帶版本的「前綴範圍到分片」對應,可單獨拆分熱門首字元而不重建無關範圍。
每個分片跨故障域複寫,請求走本地健康副本。持續記錄每前綴 QPS、快取命中率、分片 CPU、查詢 p99 和索引位元組。增加副本解決讀取負載,拆分或隔離熱門範圍解決偏斜。分片不可用時可回傳帶新鮮度指標的快取或空清單,不能借用其他語言的建議。
第七步:滿足兩套新鮮度時鐘
基礎索引每 15 分鐘重建。小型趨勢疊加層可用更短視窗聚合,並與基礎候選做有界合併,但必須通過相同的隱私與安全門檻。疊加層故障時繼續服務健康基礎索引。
政策下架由獨立分發的停用集合滿足一分鐘目標。副本在查詢後按 ID 過濾,快取鍵包含其版本;下一次基礎建置永久移除項目。如此無需為緊急下架等待大型產物重建。
第八步:驗證排序、安全與維運
壓測按實際前綴長度與語言分布重播 15 萬尖峰 QPS,涵蓋熱門單字元、冷快取啟動、副本遺失與版本切換。在服務邊界驗證 p99 低於 50 毫秒、錯誤率有界、單一回應不混合校驗和,且復原不產生驚群。
離線評估包含 Top-K 相關性、覆蓋率、重複率、語言正確性、違規內容召回及版本穩定性。線上實驗觀察搜尋完成與下游結果品質,並以無結果率、延遲、申訴和曝光集中度作護欄。點擊率受顯示位置影響,不能單獨作為結論。
演練污染事件批次、建置失敗、過大產物、熱門分片、過期停用層、部分副本上線和排序退化。每類警示對應安全動作:暫停啟用、回到舊版、隔離疊加層、拆分範圍或啟用緊急停用。
高品質示範回答
「我先把範圍定為匿名、按語言地區區分的前綴補全,每次十筆。5,000 萬使用者乘每天 10 次搜尋、每次 5 個請求,是每天 25 億次,平均約 2.89 萬 QPS;容量按題設 15 萬尖峰規劃,索引記憶體用序列化樣本實測,不猜 Trie 節點大小。
資料鏈路與服務鏈路分開。搜尋完成事件進入串流,聚合出不同使用者數、時效與結果品質。候選經過頻率、反濫用、隱私與審核,再由排序工作為每個正規化前綴寫入有界清單。產物記錄資料水位、正規化、排序與政策版本。
服務副本持有不可變版本。新版在舊版旁載入驗證,路由原子切換並可回復。請求正規化前綴與語言,查詢帶版本快取,路由至前綴分片,做一次索引查詢,再套用快速停用集合並回傳十筆。熱門單字元有專用快取,也能獨立拆分。
基礎建置滿足 15 分鐘目標,小型趨勢層可提升時效;緊急下架走一分鐘停用層,兩者失敗都不破壞最後健康基礎版。我會按 15 萬 QPS 與實際前綴分布壓測,並監控 p99、新鮮度、命中率、分片偏斜、相關性、語言串漏、安全召回及回復時間。」
常見錯誤
- 每次按鍵都查詢並排序原始紀錄 → 工作量隨歷史增長,尾端延遲不可控 → 預先計算有界 Top-K,線上只做定長查詢。
- 說「使用 Trie」就結束 → 缺少排序、發布、分片、安全與復原 → 同時說明建置與服務生命週期,並基準比較表示法。
- 用臆測節點大小估記憶體 → 編碼方式與前綴共享決定真實位元組 → 序列化代表性資料後實測。
- 快取鍵只有前綴 → 語言、政策或版本結果會串漏 → 把所有影響結果的版本放入鍵。
- 原地更新索引 → 讀者可能看到半成品或混版 → 建置不可變產物、並存載入並原子啟用。
- 只以熱門度判斷准入 → 隱私、操弄或有害文字可能出現 → 加入不同使用者門檻、反濫用、隱私和審核。
- 緊急下架也重建完整索引 → 安全期限受大型批次工作約束 → 分發快速停用層,下次建置永久移除。
- 把點擊率當無偏相關性 → 顯示位置本身影響點擊 → 結合離線判斷、護欄實驗與安全指標。
追問及應對
追問一:如何加入模糊比對?
先執行便宜的精確前綴召回,只在達到最小長度或精確覆蓋不足時觸發模糊召回。限制編輯距離和候選數,通過同一排序與審核後合併。Unicode 距離與對抗輸入必須壓測,因為擴展會增加 CPU,也可能召回敏感變體。
追問二:如何加入個人化?
先取全域候選,再混入小型、已授權的個人候選。共享快取止於混排之前,最終回應按使用者隔離且不能進入公共快取。上線前明確同意、保留、刪除、敏感查詢排除、特徵逾時與僅全域回退。
追問三:某種語言的索引放不進記憶體怎麼辦?
按實測位元組與 QPS 拆分前綴範圍,更新版本化路由對應。頂層熱門前綴放專用副本;冷範圍可在 p99 仍達標時使用記憶體映射或遠端索引。遷移隨產物版本完成,避免讀取鏈路依賴原地搬移鍵值。
追問四:突發新聞要求數秒內出現怎麼辦?
增加嚴格有界的串流疊加層,限定可信來源、高准入門檻、即時審核、TTL 與終止開關,在固定候選預算內與基礎版合併。若其時效或政策水位過期,就捨棄疊加層,繼續使用最後健康基礎版。
追問五:隱私刪除請求涉及某筆查詢怎麼辦?
按資料模型刪除或標記原始事件與聚合,把建議 ID 立即加入停用層,透過政策版本讓相關快取失效,再從修正輸入重建基礎索引。跨區域稽核傳播時間,但不能再次記錄敏感明文。