搜尋系統 (Search System)

為什麼搜尋是獨立的設計問題

LIKE '%running shoes%' 做搜尋會出兩個大問題:

  1. 無法用索引 → full table scan,幾百萬筆要跑幾十秒
  2. 只做字串比對 → 「跑步鞋」不匹配「慢跑鞋」、「running」不匹配「run」
搜尋的本質 ≠ 資料庫查詢

DB 查詢做精確比對user_id = 123);搜尋做相關性排序(找出所有和「跑步鞋」相關的,按相關度排)。需要完全不同的資料結構和查詢引擎。

面試魔法句

「搜尋我會用 Elasticsearch 建獨立的搜尋索引,資料透過 CDC + Kafka 從主資料庫非同步同步。」說出這句就展示你理解搜尋是獨立設計問題。


整體架構:兩條管線

                  ┌─ Indexing Pipeline ─┐         ┌─ Query Pipeline ─┐
主資料庫 ──CDC──► Kafka ──► Indexer ──► Elasticsearch ◄── 查詢 ── 用戶
(寫入/一致性)                          (次要索引/全文搜尋)

搜尋索引不是 primary database,而是建在主資料庫上的次要索引(secondary index)


倒排索引(搜尋引擎核心)

傳統 DB 是「文件 → 詞彙」;倒排索引反過來「詞彙 → 文件」。搜尋「跑步鞋」時找「跑步」「鞋」各自的文件清單取交集 → 速度和全表掃描不在一個量級。詳見 04-Database-Indexing/06-Inverted-Index

文字分析(建索引前的處理)

查詢時用戶輸入也經過同樣分析

確保查詢詞彙和索引詞彙能匹配。


Indexing Pipeline:資料如何進入索引(最易被忽略)

方案 機制 評價
雙寫(Dual Write) app 同時寫 DB 和 ES ❌ 兩寫非原子,DB 成功 ES 失敗就不一致;複雜度推到應用層
CDC(Change Data Capture) 監聽 DB 的 WAL → 每個變更轉事件 → 非同步同步 最推薦,大多數生產系統走這條

CDC 優點:app 只寫主 DB(搜尋同步完全解耦)、Kafka 緩衝(ES 暫不可用不丟資料)、Indexer 可做轉換。缺點:延遲幾秒~幾十秒。詳見 09-Messaging-Coord/01-Data-Pipeline

零停機重建索引(Reindex)

改 mapping(新增欄位、換分詞器)需重建整個索引。用 alias 切換:建新索引 → 灌資料 → alias 從舊指向新,搜尋零停機。


相關性排序(Relevance Ranking)

BM25(Elasticsearch 預設)

因子 含義
TF(Term Frequency) 詞在文件出現越多分數越高,但有上限(非線性)
IDF(Inverse Document Frequency) 詞越罕見分數越高(「鞋」IDF 低、「Gore-Tex」IDF 高、更有辨別力)
欄位長度 同樣出現一次,短標題權重 > 長描述

Boosting(欄位權重)

"fields": ["name^3", "category^2", "description^1"] → 名稱權重是描述的 3 倍。

業務邏輯排序

純文字相關性往往不夠。真實排序 = 相關性 + 業務指標:

最終分數 = 相關性 × 0.6 + 銷量 × 0.2 + 評分 × 0.1 + 新品加成 × 0.1

常見功能設計

功能 技術 要點
Autocomplete Edge N-gram 「running」拆成 r, ru, run, runn... 每個前綴建索引;需 100ms 內回應 → 加 Redis 快取
Faceted Search Aggregation API 側欄品牌/價格/顏色過濾,一次查詢同時回結果 + 各維度計數(Nike (143)
Pagination Search After 無限下拉用 Search After(穩定);有頁碼用 From/Size(限 offset 10,000 防深分頁)

擴展

Shard 數量建立後無法修改

資料路由依賴 shard 數量,改只能 Reindex。官方建議每 shard 10~50GB。估算:預估資料量 ÷ 目標 shard 大小(如 500GB ÷ 25GB = 20 個 primary shard)。寧可設多一點

查詢快取:熱門搜尋詞(「Nike 跑步鞋」)加 Redis 快取,key = 查詢詞 + 過濾條件,TTL 設短(幾分鐘)讓新/下架商品及時反映。


常見 Deep Dive


常見系統範例

系統 設計重點
Twitter 搜尋 MySQL → CDC → Kafka → ES;對內容/用戶名/hashtag 多欄位搜尋,用時間 + 熱度 boosting
電商搜尋 BM25 基礎 × 銷量/評分;Aggregation 做過濾;Edge n-gram + Redis 做 autocomplete
Airbnb 搜尋 geo_distance 地理過濾;日期可用性用 nested/join 結構