天天看點

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

導讀:阿裡主搜(淘寶天貓搜尋)是搜尋離線平台非常重要的一個業務,具有資料量大、一對多的表很多、源表的總數多和熱點資料等特性。對于将主搜這種邏輯複雜的大資料量應用遷移到搜尋離線平台總是不缺少性能的挑戰,搜尋離線平台經過哪些優化最終實作全量高吞吐、增量低延遲的呢?文章大綱如下:
  1. 前言
  2. 搜尋離線平台基本概念
  3. 主搜業務特點與性能要求
  4. Blink Job 性能調優詳解
  5. 結語
本文将與大家分享阿裡主搜在實作全量高吞吐、增量低延遲方面的優化經驗,希望對大家的 Flink 應用有所幫助。

一.前言

在阿裡搜尋工程體系中我們把搜尋引擎、線上算分等毫秒級響應使用者請求的服務稱之為“線上”服務;與之相對應的,将各種來源資料轉換處理後送入搜尋引擎等“線上”服務的系統統稱為“離線”系統。搜尋離線平台作為搜尋引擎的資料提供方,是集團各業務接入搜尋的必經之路,也是整個搜尋鍊路上極為重要的一環,離線産出資料的品質和速度直接影響到下遊業務的使用者體驗。

搜尋離線平台經過多年沉澱,不僅承載了集團内大量搜尋業務,在雲上也有不少彈外客戶,随着平台功能的豐富,Blink(阿裡内部版本的 Flink) 版本的領先,我們在 2019 年年初開始計劃把主搜(淘寶天貓搜尋)遷移到搜尋離線平台上。

主搜在遷移搜尋離線平台之前的架構具有架構老化、Blink 版本低、運維困難、計算架構不統一等不少缺點,随着老主搜人員流失以及運維難度與日俱增,重構工作早已迫上眉睫。

對于将主搜這種邏輯複雜的 X 億資料量級應用遷移到搜尋離線平台總是不缺少性能的挑戰,業務特點與性能要求決定了主搜上平台的過程中每一步都會很艱辛。為了讓性能達到要求,我們幾乎對每個 Blink Job 都進行了單獨調優,最初的理想與最後的結局都是美好的,但過程卻是極其曲折的,本文将主要介紹主搜在遷移搜尋離線平台過程中在性能調優方面具體做了哪些嘗試。

主搜遷移搜尋離線平台的完成對于平台來說有裡程碑式的意義,代表搜尋離線平台有能力承接超大型業務。

二.搜尋離線平台基本概念

搜尋離線平台處理一次主搜全增量主要由同步層和資料處理層組成,它們又分别包括全量和增量流程。為了讀者更好了解下文,先簡單介紹幾個關于搜尋離線平台的基本概念。

1.集團内支撐業務

目前搜尋離線平台在集團内支援了包括主搜,AE 在内的幾百個業務。其中資料量最大的為淘寶天貓評價業務,資料量達到了 X 百億條,每條資料近上 X 個字段。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

2.場景

處理使用者的資料源(MySQL 或 ODPS)表,将資料經過一系列的離線處理流程,最終導入到 HA3 線上搜尋引擎或 ES 中。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

3.平台相關技術棧

如下圖,搜尋離線平台目前資料存儲基于 HDFS/盤古,資源排程依賴于 YARN 或 Hippo,計算架構統一用 Flink/Blink 執行。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

4.全量

全量是指将搜尋業務資料全部重新處理生成,并傳送給線上引擎,一般是每天一次。

這麼做有兩個原因:有業務資料是 Daily 更新;引擎需要全量資料來高效的進行索引整理和預處理,提高線上服務效率。全量主要分為同步層與資料處理層。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

5.增量

增量是指将上遊資料源實時發生的資料變化更新到線上引擎中。

這也就意味着在我們的場景中對于增量資料不需要保證 Exactly Once 語義,隻需要保證 At Least Once 語義。基于該背景,我們才能用全鍊路異步化的思維來解一對多問題(下文會詳細講解)。

與全量一樣,增量也分為同步層與資料處理層。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

6.一對多

在搜尋這個領域某些業務資料需要用一對多的形式來描述,比如商品寶貝和 SKU 的關系即是個典型的一對多資料的例子。在搜尋離線基于 Hologres(阿裡巴巴自研分布式資料庫)存儲的架構中,一對多的資料存儲在單獨的一張雙 pk 的 HoloTable 中,第一、二主鍵分别的寶貝 ID 與 SKU_ID。

有了上面這些概念之後,在後續的段落中我們會看到搜尋離線平台針對主搜各 Blink Job 的性能調優,先簡要概括下主搜業務特點與性能要求。

7.資料存儲方式

搜尋離線平台以前用 HBase 做鏡像表時,是用一張多列族大寬表來存儲業務單次元所有資料。經過詳細調研之後,我們決定用 Hologres 替換 HBase,是以需要對存儲架構做全面的重構。用多表來模拟 HBase 中的多列族,單 HoloTable 中包括很多業務資料源表的資料。重構後的資料存儲方式大緻如下:

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?
百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

8.同步層

所謂同步層,一般是将上遊資料源的資料同步到鏡像表,供資料處理層高效處理。由于業務方單次元的資料有很多 MySQL 表或 ODPS 表組成,少則 X 張,多則像主搜這樣 X 張。是以将同緯度資料聚合到一張 Holo 表中時,如果多張表兩兩 join 的話會産生大量 shuffle,是以我們采取異步 upsert 方式,不同資料源表的資料寫 Holo 表中不同的列來解決海量資料導入問題。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?
百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

9.資料處理層

所謂資料處理層,是指将同步層得到的各鏡像表(HBase/Holo)的資料進行計算,一般包括多表 Join、UDTF 等,以友善搜尋業務的開發和接入。

三.主搜業務特點與性能要求

下面首先介紹下主搜業務特點與性能要求,再詳細介紹我們進行了怎樣的調優才達到了性能的要求。

1.主搜業務特點

  • 資料量大

主搜有 X 億(有效的 X 億)個商品,也就是主次元有 X 億條資料,相比于平台其他業務(除淘寶評價業務)多出 X 個數量級。這麼多資料我們能否在 X 個多小時完成全量?如何實作高吞吐?挑戰非常大。

  • 一對多的表很多

主搜業務有很多一對多的表需要 Join,例如一個商品對應多個 SKU,部分商品對應了接近 X 個 SKU 資訊。這些資訊如何能夠高性能的轉換為商品次元,并與商品資訊關聯?

  • 源表的總數多

主搜有 X 多張表(包括一對多的表),平台其他業務的源表個數一般都在個位數。源表數量多會導緻一系列的問題,比如讀取 ODPS 資料時如何避免觸發 ODPS 的限制?拉取大表資料時如何做到高吞吐?這些問題都需要我們一一解決。

  • 熱點資料

主搜有一些大賣家(餓了麼,盒馬等)對應了很多商品,導緻在資料處理層出現非常嚴重的資料傾斜等問題。如何解決大資料處理方向經常出現的 SKEW?

2.主搜性能要求

  • 全量(同步層 + 資料處理層)高吞吐!

全量要求每天一次,在有限的資源情況下每次處理 X 億的商品,這麼大的資料量,如何實作高吞吐,挑戰非常大!

  • 增量(同步層 + 資料處理層)低延遲!

增量要在 TPS 為 X W 的情況下達到秒級低延遲,并且雙 11 期間有部分表(例如 XX 表)的 TPS 能達到 X W,增量如何保證穩定的低延遲?值得思考!

下面一一描述我們是如何解決這些問題來達到性能要求的。

四.Blink Job 性能調優詳解

根據上述主搜業務特點與性能要求羅列出下圖,左邊與中間兩清單示主搜哪些特點導緻某階段任務性能差。是以我們要對相應階段 Blink Job 進行調優,調優完成也就代表着平台能滿足圖中最右邊一列主搜所需要的全量高吞吐與增量低延遲的性能要求。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

下面按照全量,增量,解一對多問題的脈絡來給大家介紹我們是如何解決上述五個問題之後達到全量高吞吐以及增量低延遲的性能要求的。

1.全量高吞吐性能調優

全量主要包括同步層與資料處理層,必須實作高吞吐才能讓全量在 X 個多小時之内完成。同步層在短時間内要同步約X張表中的上 X 億全量資料,且不影響同時在運作的增量時效性是一個巨大的挑戰。資料處理層要在短時間内處理X多億條資料,Join 很多張鏡像表,以及 UDTF 處理,MultiGet 等,最後産生全量 HDFS 檔案,優化過程一度讓人頻臨放棄。這裡重點介紹資料處理層的性能調優曆程。

該 Job 的調優曆時較長,嘗試方案較多,下面按照時間順序講解。

  • 初始形态

首先提一下 IC 次元為商品次元,UIC 次元為賣家次元,并且最開始我們的方案是沒有 FullDynamicNestedAggregation 和 IncDynamicNestedAggregation 的(後文會詳細提到這兩個Job)。Scan IC 次元單 Pk 表之後做一系列的 DImJoin、UDTF、MultiJoin。在測試過程中發現 DimJoin 多 pk 表(一對多表)的資料時,性能非常低下,全鍊路 Async 的流程退化成了 Sync,原因是我們一對多的資料存在單獨的一個 SaroTable(對多個 HoloTable 的邏輯抽象)中,對指定第一 pk 來取對應所有資料用的是 Partial Scan,這是完全 Sync 的,每 Get 一次都要建立一個 Scanner,雖然我們不但對于 DimJoin 加了 Cache,并且對于主搜特有的 MultiGet 也加了對于 SubKey 的精準 Cache。但是測試下來發現,性能還是完全得不到滿足,是以嘗試繼續優化。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?
  • 引入 LocalJoin 與 SortMergeJoin

由于性能瓶頸是在 DimJoin 多 pk 的 SaroTable 這裡,是以我們想辦法把這部分去掉。由于一對多的 SaroTable 隻有兩個次元具有,是以我們嘗試先分别将 IC 次元與 UIC 次元的所有表(包括單 pk 與多 pk)進行 LocalJoin,結果再進行 SortMergeJoin,然後繼續别的流程。

首先介紹下 Local Join。由于 HoloStore 保證相同 DB 中所有表都是按照相同的 Partition 政策,并且都是按照主鍵字典序排好序的,是以我們可以将同緯度同 Partition 的資料拉取到一個程序中進行 Join,避免了 Shuffle,如下圖所示。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

是以拓撲大概變為:

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

經過測試,由于業務上面存在大賣家(一個賣家有很多商品),導緻 SortMergeJoin 之後會有很嚴重的長尾,如下圖所示,Uid 為 101 與 103 的資料都是落到同一個并發中,我曾經嘗試再這個基礎之上再加一層 PartitionBy nid 打散,發現無濟于事,因為 SortMergeJoin 的 Sort 階段以及 External Shuflle 對于大資料量的 Task 需要多次進行 Disk File Merge,是以該長尾 Task 還是需要很長時間才能 Finish。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?
  • 加鹽打散大賣家

是以我們需要繼續調優。經過組内讨論我們決定對大賣家進行加鹽打散,從 ODPS 源表中找出 Top X 的大賣家 ID,然後分别在主輔次元 Scan + Local Join 之後分别加上 UDF 與 UDTF,具體流程圖與原理示例見下面兩幅圖:

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?
百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

如上圖所示,Uid 為 101 與 103 的資料被打散到多個并發中了,并且因為我們在 SortMergeJoin 之後加了 UDTF 把加的 Salt 去掉,是以最終資料不會有任何影響。

  • 最終形态

這樣全量 FullJoin 總算完成了,并且性能也勉強達标,是以我們開始調整增量流程(IncJoin),這時發現 IncJoin 跟 FullJoin 的初始形态存在一樣的問題,追增量非常慢,永遠追不上,是以組内讨論之後決定在同步層針對全量新增一個 FullDynamicNestedAggregation Job(下文會詳細提到),這是一個 Blink Batch Job 它将各次元一對多的 SaroTable 資料寫到對應次元的主表中,然後在 FullJoin 最開始 Scan 時一起 Scan 出來,這樣就避免了 DimJoin 多 pk 的 SaroTable。最終達到了全量高吞吐的要求,全量 FullJoin 最終形态如下:

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

2.增量低延遲性能調優

增量性能主要受困于資料處理層 IncJoin,該 Job 最開始是一個 Blink Stream Job,主要是從 SwiftQueue 中讀出增量消息再關聯各個鏡像表中的資料來補全字段,以及對資料進行 UDTF 處理等,最後将增量消息發往線上引擎 SwiftQueue 中。

基于“流批一體”的思想,經過一系列嘗試,我們增量資料處理層 Job 的最終形态如下。

與全量不同的是由于增量是實時更新的,是以更新記錄不僅要寫到 Swift Queue 中,還要寫入 SaroTable 中。另外,我們根據業務特點給各個 Job 分别加了按 pk 對記錄去重的 window。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

3.解一對多問題

主搜有很多一對多的表,在資料處理層如何高效的将資料 Get 出來轉換為主次元之後進行字段補全,困擾我們很久。

為了提升效率我們必須想辦法提升 Cpu 使用率。是以 Get 記錄改為全鍊路異步來實作,由于我們一對多資料存在多 pk 的 HoloTable 中,指定第一 pk 去擷取相關資料在 Holo 服務端是以 Scan 來實作的。這樣由于異步程式設計的傳染性,全鍊路異步會退化為同步,性能完全不達标。

  • 解決方法

為了将“僞異步”變成真正的全鍊路異步,經過多次讨論與實踐之後,我們決定将一對多表中相同第一 pk 的多條資料 Scan 出來 GroupBy 為一條資料,将每個字段轉化為 Json 之後再 Put 進主表中,主要步驟如下圖所示。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

我們針對全量與增量在同步層加 Job 來解決,分别為 FullDynamicNestedAggregation(Blink Batch Job)與 IncDynamicNestedAggregation(Blink Stream Job),這兩個 Job 大緻流程為如下圖所示。

百萬TPS高吞吐、秒級低延遲,阿裡​搜尋離線平台如何實作?

值得一提的是,正如前文介紹增量時提到的背景,我們的場景中對于增量資料不需要保證 Exactly Once 語義,隻需要保證 At Least Once 語義。是以基于該背景,我們能夠将資料處理層增量 Job 拆分為兩個 Job 執行,一對多的問題得以解決。

這樣我們在資料處理層就不需要去 Scan HoloTable 了,進而可以用全鍊路異步化的方式來提升增量整體性能。

  • 截斷優化

為了避免将多條資料轉為一條資料之後由于資料量過大導緻 FullGC 的“大行”問題。基于業務的特性,我們對于每個一對多表在 Scan 時支援截斷功能,對于相同的第一 pk 記錄,隻 Scan 一定條數的記錄出來組裝為 Json,并且可以針對不同的表實作白名單配置。

  • 加過濾 Window 優化

針對業務的特點,一對多的很多表雖然可以接受一定時間的延遲,但是為了避免對離線系統以及線上 BuildService 造成太大的沖擊,是以更新不能太多,是以我們加了 30min 的去重視窗,這個視窗作用非常大,平均去重率高達 X% 以上。

五.結語

經過一系列優化,主搜不僅在資源上相對于老架構有不少的節省,而且同時實作全量高吞吐與增量低延遲,并且在 2019 年度雙 11 零點應對突增流量時表現的遊刃有餘。

對系統進行性能調優是極其複雜且較精細的工作,非常具有技術挑戰性。不僅需要對所選用技術工具(Flink/Blink)熟悉,而且對于業務也必須了解。加 window,截斷優化,加鹽打散大賣家等正是因為業務場景能容忍這些方法所帶來的相應缺點才能做的。

除了本文提到的調優經驗,我們對同步層全增量 Job 與 MultiGet 也進行了不少調優,篇幅原因與二八原則這裡就不詳細介紹了。

主搜成功遷移也使得搜尋離線平台完成了最後一塊拼圖,成為阿裡巴巴集團搜尋中台以及核心鍊路的基礎子產品。

作者簡介:

王偉駿,花名鴻曆,阿裡巴巴搜尋推薦事業部進階開發工程師。2016 年碩士畢業于南京郵電大學。Apache Hadoop & Flink & Eagle Contributor。目前負責阿裡巴巴搜尋離線平台 Runtime 層相關工作。

另外,陳華曦(昆侖)給了本文很多建議,文中部分圖由李國鼎(石及)貢獻。