【導讀】Uber 如何使用 OpenSearch 驅動十億級向量搜尋 Powering Billion-Scale Vector Search with OpenSearch

這篇文章適合搜尋引擎工程師、後端架構師以及正在處理大規模向量檢索(Vector Search)團隊閱讀,深入解析 Uber 如何解決 15 億級別數據的寫入瓶頸與查詢延遲問題。

三個精華點:

  • 技術選型轉向 OpenSearch:Uber 原本使用 Apache Lucene,但受限於算法單一與缺乏原生 GPU 支援,無法滿足日益複雜的語意搜尋需求。轉向 OpenSearch 後,利用其支援多種 ANN(近似最近鄰)算法的靈活性與未來對 FAISS (GPU 加速) 的整合潛力,解決了擴展性問題。

  • 寫入效能優化(提速 79%):面對 15 億筆向量數據,預設設定導致索引建立需耗時 12.5 小時。團隊透過「增加 Spark 並發數」、「關閉預設的每秒 Refresh (改為 -1)」、「優化 Merge 與 Flush 策略」以及「移除 _sourcedoc_values 縮減索引大小」,將時間大幅縮短至 2.5 小時。

  • 查詢延遲減半與藍綠部署:為了達到 P99 < 120ms 的目標,Uber 發現「Shard 數量等於節點數量」時效能最佳,並透過增加 Replica 來平滑延遲。此外,為了解決建索引時搶占資源的問題,他們採用了藍綠部署(Blue/Green Deployment),在獨立集群建立索引後再切換流量,確保線上服務零停機且不掉速。

這篇文章提到的一個核心痛點:「預設值永遠只適合 Hello World,不適合 Production」。

在處理像 Elasticsearch 或 OpenSearch 這類搜尋引擎時,許多人都有過類似的慘痛經驗:在資料量小的時候一切完美,但當數據量上億時,預設的 refresh_interval (1秒) 和激進的 segment merge 策略往往會導致磁碟 I/O 爆炸,甚至讓整個 Cluster 卡死(Stop-the-world)。

Uber 這裡展示的優化手段非常教科書級且實用,特別是他們對於 I/O Amplification(I/O 放大)的觀察,以及果斷放棄 _source 欄位來換取效能的取捨(Trade-off)。此外,他們提到的「藍綠部署」策略雖然聽起來是運維層面的事,但對於搜尋業務來說至關重要——永遠不要讓繁重的寫入任務(Indexing)去拖垮使用者的查詢體驗(Querying)。這篇文章是從「堪用」到「高性能」的最佳實踐範本。

為了像我一樣,一開始對技術名詞一片空白的朋友,而準備的「白話文」技術名詞解釋。這些詞彙雖然聽起來很艱深,但其背後的邏輯其實跟日常生活非常貼近。


1. ANN (Approximate Nearest Neighbor)

  • 白話翻譯: 「差不多準確」的快速搜尋法。
  • 情境解釋: 想像你在一個巨大的圖書館找一本「關於太空冒險」的書。
    • 傳統精確搜尋 (KNN): 你必須把圖書館裡幾百萬本書每一本都拿起來看,確保找到那本「最最最」符合的書。這非常精準,但可能要花好幾年(電腦會算太久)。
    • ANN: 你直接問圖書管理員:「科幻小說區在哪?」然後直接去那個架子上挑一本最順眼的。雖然你可能錯過了一本躲在角落的絕世好書,但你只花了 1 分鐘就找到了「足夠好」的書。
    • 重點: 在大數據時代,為了「快」,我們願意犧牲一點點「精準度」。

2. FAISS

  • 白話翻譯: Facebook 開發的「超級找書機器人」。
  • 情境解釋: 如果 ANN 是「找書的方法」,那 FAISS 就是 Facebook (Meta) 寫好的一套超強工具庫(Library)。它就像是一個受過特種訓練的圖書管理員,動作極快,而且還懂得用 GPU(顯示卡)來加速。大家不用從頭寫程式去實作 ANN,直接用 FAISS 就好了。

3. Spark 並發數 (Concurrency)

  • 白話翻譯: 同一時間有多少個工人在搬磚。
  • 情境解釋: Spark 是一個處理大數據的工具。
    • 低並發: 只有 1 個工人在搬 1000 箱貨物(這個工人已沒有靈魂)。
    • 高並發: 老闆良心發現,請了 100 個工人同時搬貨,效率瞬間提升 100 倍。
    • Uber 做的優化: 就是「多請一些工人」,讓電腦同時開更多條線程(Threads)來處理資料寫入。

4. Refresh (改為 -1)

  • 白話翻譯: 暫停「即時更新」,等貨都補滿了再開門。
  • 情境解釋: 搜尋引擎(如 OpenSearch)預設每 1 秒會「Refresh」一次,就像便利商店店員每 1 秒就跑去整理架上的貨,讓你隨時都能買到剛上架的東西。
    • OpenSearch 的運作機制: 貨車(寫入數據)把貨卸在倉庫(記憶體)時,客人(搜尋者)在賣場是看不到這些貨的。必須經過一個叫 Refresh 的動作,店員才會把貨從倉庫搬到賣場架上,這時客人才搜得到。
    • 預設值 (每 1 秒 Refresh): 就像貨車每卸下一罐可樂,店員就立刻暫停卸貨,拿著這一罐跑去賣場上架。
    • 壞處: 店員一直在跑來跑去(消耗 CPU),導致貨車卸貨很慢。
    • 優化後 (Refresh = -1): 店長下令:「現在開始,不要把貨拿到賣場!先把鐵門拉下來,專心把十億罐可樂全部卸貨堆在倉庫裡。等全部卸完了,我們再一次全部上架。」
    • 好處: 少了來回跑動的時間,卸貨(建立索引)的速度就飛快了。

5. 優化 Merge 與 Flush 策略

  • 白話翻譯: 優化「打掃」與「整理」檔案的頻率。
  • 情境解釋:
    • Flush (沖刷): 就像你寫小抄,要影印給要被二一的同學,而寫在便條紙上(記憶體),那Flush 就是影印好的紙(硬碟)。就像寫一個字就跑去影印機印一次,影印機(磁碟 I/O)絕對會過熱罷工。優化後變成「寫滿一張紙再影印」,減少動作次數。
    • Merge (合併): 小抄上有很多零散的頁面(Segment),Merge 就是把這些散亂的頁面重新謄寫成一本厚實的書。Uber 調整策略就是告訴系統:「現在先別急著整理書(合併),先把內容抄完再說」。因為「整理書」這個動作非常花腦力(CPU),如果在趕著寫小抄的時候還要一邊整理書,速度一定變慢。

6. Segment Merge 策略 (補充說明)

  • 白話翻譯: 把很多小箱子打包成大箱子的規則。
  • 情境解釋: 假如你要查「去年在 7-11 花了多少錢?」
    • 合併前(許多 Small Segments): 你的抽屜裡塞滿了 365 張皺巴巴的發票(就像 365 個小檔案)。
    • 搜尋過程: 你必須把這 365 張發票一張一張攤開來看,一張一張加總。
    • 缺點: 動作很慢,而且翻找的過程很吵(磁碟讀取次數多)。
  • 合併後(Merged Segment): 系統在背景很貼心地幫你把這 365 張發票的內容,整理並抄寫到 1 本年度記帳本 上(這就是合併成 1 個大檔案)。
    • 搜尋過程: 你只需要打開這 1 本 記帳本,一眼就看到總金額。
    • 效益(Benefit):
      1. 搜尋速度變超快: 從「翻 365 次並加總」變成「翻 1 次得到關鍵數字」。
      2. 省空間: 丟掉那些皺巴巴的發票紙張(刪除重複或過期的標記),只留乾淨的紀錄。
    • 為什麼要優化? 因為 Uber 現在正忙著把十億張發票塞進抽屜,如果這時候還要分心去「整理記帳本」,會佔用大量的 CPU 和磁碟讀寫,手會打結,導致卡頓。所以 Uber 的策略是:**寫入時先別太頻繁合併(先亂塞沒關係),等寫完了再慢慢整理。

7. P99 < 120ms 的目標

  • 白話翻譯: 保證 99% 的人都覺得「很快」(0.12 秒內)。
  • 情境解釋:
    • 平均值 (Average) 的陷阱: 如果馬斯克走進平民窟,大家的「平均資產」都會破億,但這不代表大家都有錢。
    • P99 (第 99 百分位數): 我們把 100 個使用者的查詢速度從快排到慢。第 99 個人的速度代表了「絕大多數人的體驗」。
    • 含義: 只要 P99 < 120ms,代表 100 個人裡面,有 99 個人的搜尋結果都在 0.12 秒內跑出來。我們允許那第 100 個人(可能是網路爛)慢一點,但我們要保證絕大多數人的體驗是非常絲滑的。

8. Blue/Green Deployment (藍綠部署)

  • 白話翻譯: 蓋兩間一模一樣的店,裝修好新店再把客人導過去。
  • 情境解釋: 你要升級系統或重建索引(Index)。
    • 傳統做法: 讓店裡一邊施工一邊營業,客人會覺得很吵、塵土飛揚(系統變慢、報錯)。
    • 藍綠部署:
      • Blue (舊環境): 目前正在營業的店,客人繼續用。
      • Green (新環境): 在旁邊蓋一間全新的店,關門偷偷把幾十億筆資料灌進去。
      • 切換: 等 Green 準備好了,只要把門口的指路牌(Load Balancer)一轉,所有客人瞬間引導到新店。舊店就可以關掉或留著備用。過程完全無感。

9. 什麼是「預設值永遠只適合 Hello World,不適合 Production」

  • 白話翻譯: 原廠設定是給新手練習用的,不是給職業選手比賽用的。
  • 情境解釋:
    • Hello World: 程式設計的第一課,代表「最小規模的測試」。
    • Production: 真實戰場,像 Uber 這種幾億人在用的系統。
    • 比喻: 你買了一台電腦,預設是「省電模式」。這對只拿來打字的人(Hello World)很好。但如果你要拿來跑 3D 遊戲大作(Production),你不把省電模式關掉、不把風扇開到最強,電腦一定會過熱當機。軟體的預設值通常是為了「方便、省資源、不出錯」,而不是為了「極致效能」。

原文連結