Uber 如何大規模完成工作統計

介紹

優步的運營規模龐大,每個季度爲超過 22 億次出行提供便利。即使是簡單的見解,也需要一個規模化的解決方案。在我們的案例中,我們需要計算某人在 Uber 平臺上參與的工作數量,在任意時間窗口內。本文重點介紹在將 Apache Pinot™ 集成到我們的解決方案中時面臨的挑戰和吸取的經驗教訓。

背景

具體來說,我們的解決方案需要解決以下問題:

我們之前的解決方案很簡單:檢索頁面大小限制爲 50 的給定主題的作業,然後對結果進行分頁,直到沒有其他作業。在 Uber 的早期,沒有一個賬戶可以累積相對較長的任期,這運作良好。然而,Uber 涉足新的垂直領域,一些賬戶開始出現數以萬計的任期,很明顯我們需要一個更強大的解決方案。

特定於旅行的任期是一個狹窄的用例

一個主要的產品要求是,該解決方案必須能夠計算任期回溯。這本身是站得住腳的,但伴隨着我們的數據保留政策,我們的下游團隊認爲適應是不合理的。

撤銷超過 2 年的數據

爲了節省成本,同一團隊確定將超過 2 年的數據隔離到延遲更高的存儲層中。然而,在項目中期計劃的變化導致他們完全放棄了對這些數據的在線訪問。

降低複雜性

我們當時的解決方案需要爲 Uber 的每個市場拼接三個數據源:Rides 和 Eats。

更廣泛的團隊中的類似用例

在向更廣泛的團隊展示我們的設計後,我們發現 Uber 的團隊都有類似的相鄰項目,每個項目都有類似的要求,每個項目都重新實施了自己的解決方案,以獨立計算工作任期。考慮到這些限制,我們考慮了幾種架構。我們認真考慮過的一個方案得到了 Apache Hive™ 和 Docstore(Uber 的內部分佈式數據庫)的支持,最終選擇了利用 Apache Pinot™ 的解決方案。這裏一個有趣的功能是混合表,它提供了一個無縫的界面,可以將實時和離線數據拼接在一起。

架構

發展挑戰

Apache Pinot™ 是一個非常強大的產品,爲我們的架構提供了無與倫比的靈活性。然而,我們在此過程中遇到了幾個障礙,下面,我們將詳細介紹挑戰和我們解決這些問題的方法,這可能會對讀者有所啓發。

挑戰 #1a – 容量規劃

我們面臨的第一個挑戰是制定容量要求,以配置我們的專用租戶所需的硬件。Apache Pinot™ 使用壓縮技術,因此很難先驗地測量磁盤上的空間。在這裏,我們選擇採用 10% 的樣本數據集,在我們的例子中,根據樣本佔用的空間預測磁盤使用情況就足夠了。

挑戰 #1b – 查詢性能

這裏一個有趣的是,雖然我們能夠通過採樣來估計磁盤上的數據集大小,但我們無法準確預測查詢性能。在這種情況下,我們必須等到擴展到整個數據集之後,才能獲得關鍵指標,例如 p99 讀取時間、磁盤讀取吞吐量等。一旦我們能夠做到這一點,生產規模的流量就會使我們的專用集羣陷入癱瘓,讀取時間超過 10 秒的代理限制,SSD 的讀取吞吐量和 CPU 使用率達到最大。我們立即着手研究優化,因爲這並不意外,因爲每個查詢都相當於加載到全表掃描。作爲參考,我們的查詢的 SQL 爲:

SELECT
    *
FROM
    pinot_hybrid_table
WHERE  provider_id = ‘…’
    AND requester_id = ‘…’
    AND timestamp >=  AND timestamp <= 

這讓團隊感到擔憂,我們採取了幾個步驟來提高績效,包括:

WQ6Usl

圖 3:通過 Pinot 查詢控制檯實施布隆過濾器之前和之後的查詢統計信息

挑戰 #2 – 業務邊緣案例

隨着 Uber 繼續向平臺添加功能,它們對源數據的下游影響也在增加。目前,我們可以通過三種模式接收行程級別的信息:Apache Hive™、Apache Kafka™ Topic 和 API 響應,每種模式都有不同的架構。以合理的方式將新功能改造和表示到現有架構中可能很困難,尤其是 Apache Hive™ 架構。歷史數據具有很大的慣性,可能會使遷移架構變得不合理。例如,考慮 “票價分割” 功能,其中乘車費用可以在多個乘客之間分攤。在此功能之前,一項工作始終只有一個騎手,該騎手始終是付款人,並且每個 Hive 記錄都暗示司機在訂單上執行了一項工作。這些不變性不再成立。此處選擇複製記錄並將狀態設置爲 FARE_SPLIT,同時_將 driver_uuid_列設置爲 NULL。正是這樣的複雜性使得執行簡單的 _COUNT DISTINCT _聚合變得不可能。

挑戰 #3 – 慢數據

我們在這裏必須解決的另一個挑戰是上游數據的速度緩慢。雖然大多數數據在幾秒鐘內到達,但行程可能在長達一週的時間內不會顯示在上游數據源中。爲了解決這個問題,我們創建了一個管道,該管道動態生成回填管道,但只計劃在 T - 7d 和 T 之間運行。除此之外,我們還執行離線數據質量檢查,這是對 T - 1d 日期的簡單 count(*) 查詢,以確保我們的源 Apache Hive™ 表與 Apache Pinot™ 混合表的結果同步。

挑戰 #4 – 突發性上游負載

具有尖峯流量模式的上游流量也是一個問題。在這種情況下,特定的速率限制實現導致每 10 秒的流量激增,每次流量都違反了 10 秒的 Apache Pinot™ 代理超時,導致請求失敗。

我們通過在請求時向上遊客戶端添加抖動來解決這個問題,以便隨着時間的推移更均勻地分佈我們的查詢。在克服了這些挑戰之後,我們已經爲實時生產流量提供服務了近一年,執行負載測試顯示,在包含故障轉移流量緩衝區後,至少有 200% 的餘量。我們的 p99 讀取延遲爲 ~1 秒:令人印象深刻,因爲我們的一些上游查詢可能會達到 2,000 多個段,每個段消耗大約 90 MB 的磁盤空間。在迭代我們的解決方案後,我們開始發佈將我們之前的下游與我們的解決方案進行比較的指標。我們基於 Apache Pinot™ 的解決方案提供了幾乎 1:1 的準確度,讓我們有信心依賴它。我們首先使用 config 標誌對更改進行門控,一段時間後,完全切換並棄用了之前的實現。

結論

通過一些簡單的補充,我們有信心能夠回答更有力的問題。例如:

在工作粒度上找到時間點任期的成本很高。在這方面,提高性能和降低存儲成本的途徑仍在探索之中。雖然仍處於設計過程中,但我們預計通過降低作業粒度要求,我們應該能夠顯著提高讀取吞吐量。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/p8ZF_hJ7xdX0pvzbfASV9g