日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

芊寶寶最可愛發表於2019-12-30
導讀:當今生活節奏日益加快,企業面對不斷增加的海量資訊,其資訊篩選和處理效率低下的困擾與日俱增。由於使用者營銷不夠細化,企業 App 中許多不合時宜或不合偏好的訊息推送很大程度上影響了使用者體驗,甚至引發了使用者流失。在此背景下,友信金服公司推行全域的資料體系戰略,透過打通和整合集團各個業務線資料,利用大資料、人工智慧等技術構建統一的資料資產,如 ID-Mapping、使用者標籤等。友信金服使用者畫像專案正是以此為背景成立,旨在實現“資料驅動業務與運營”的集團戰略。目前該系統支援日處理資料量超 10 億,接入上百種合規資料來源。

一、技術選型

傳統基於 Hadoop 生態的離線資料儲存計算方案已在業界大規模應用,但受制於離線計算的高時延性,越來越多的資料應用場景已從離線轉為實時。這裡引用一張表格對目前主流的實時計算框架做個對比。

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

Apache Storm 的容錯機制需要對每條資料進行應答(ACK),因此其吞吐量備受影響,在資料大吞吐量的場景下會有問題,因此不適用此專案的需求。

Apache Spark 總體生態更為完善,且在機器學習的整合和應用性暫時領先,但 Spark 底層還是採用微批(Micro Batching)處理的形式。

Apache Flink 在流式計算上有明顯優勢:首先其流式計算屬於真正意義上的單條處理,即每一條資料都會觸發計算。在這一點上明顯與 Spark 的微批流式處理方式不同。其次,Flink 的容錯機制較為輕量,對吞吐量影響較小,使得 Flink 可以達到很高的吞吐量。最後 Flink 還擁有易用性高,部署簡單等優勢。相比之下我們最終決定採用基於 Flink 的架構方案。

二、使用者畫像業務架構

使用者畫像系統目前為集團線上業務提供使用者實時標籤資料服務。為此我們的服務需要打通多種資料來源,對海量的數字資訊進行實時不間斷的資料清洗、聚類、分析,從而將它們抽象成標籤,並最終為應用方提供高質量的標籤服務。在此背景下,我們設計使用者畫像系統的整體架構如下圖所示:

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

整體架構分為五層:

  1. 接入層:接入原始資料並對其進行處理,如 Kafka、Hive、檔案等。
  2. 計算層:選用 Flink 作為實時計算框架,對實時資料進行清洗,關聯等操作。
  3. 儲存層:對清洗完成的資料進行資料儲存,我們對此進行了實時使用者畫像的模型分層與構建,將不同應用場景的資料分別儲存在如 Phoenix,HBase,HDFS,Kafka 等。
  4. 服務層:對外提供統一的資料查詢服務,支援從底層明細資料到聚合層資料的多維計算服務。
  5. 應用層:以統一查詢服務對各個業務線資料場景進行支撐。目前業務主要包含使用者興趣分、使用者質量分、使用者的事實資訊等資料。

三、使用者畫像資料處理流程

在整體架構設計方案設計完成之後,我們針對資料也設計了詳盡的處理方案。在資料處理階段,鑑於 Kafka 高吞吐量、高穩定性的特點,我們的使用者畫像系統統一採用 Kafka 作為分散式釋出訂閱訊息系統。資料清洗階段利用 Flink 來實現使用者唯一性識別、行為資料的清洗等,去除冗餘資料。這一過程支援互動計算和多種複雜演算法,並支援資料實時 / 離線計算。目前我們資料處理流程迭代了兩版,具體方案如下:

1.0 版資料處理流程

資料接入、計算、儲存三層處理流程

整體資料來源包含兩種:

  1. 歷史資料:從外部資料來源接入的海量歷史業務資料。接入後經過 ETL 處理,進入使用者畫像底層資料表。
  2. 實時資料:從外部資料來源接入的實時業務資料,如使用者行為埋點資料,風控資料等。

根據不同業務的指標需求我們直接從集團資料倉儲抽取資料並落入 Kafka,或者直接從業務端以 CDC(Capture Data Change)的方式寫入 Kafka。在計算層,資料被匯入到 Flink 中,透過 DataStream 生成 ID-Mapping、使用者標籤碎片等資料,然後將生成資料存入 JanusGraph(JanusGraph 是以 HBase 作為後端儲存的圖資料庫介質)與 Kafka,並由 Flink 消費落入 Kafka 的使用者標籤碎片資料,進行聚合生成最新的使用者標籤碎片(使用者標籤碎片是由使用者畫像系統獲取來自多種渠道的碎片化資料塊處理後生成的)。

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

資料服務層處理流程

服務層將儲存層儲存的使用者標籤碎片資料,透過 JanusGraph Spark On Yarn 模式,執行 TinkerPop OLAP 計算生成全量使用者 Yids 列表檔案。Yid 是使用者畫像系統中定義的集團級使用者 ID 標識。結合 Yids 列表檔案,在 Flink 中批次讀取 HBase 聚合成完整使用者畫像資料,生成 HDFS 檔案,再透過 Flink 批次操作新生成的資料生成使用者評分預測標籤,將使用者評分預測標籤落入 Phoenix,之後資料便可透過統一資料服務介面進行獲取。下圖完整地展示了這一流程。

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

ID-Mapping 資料結構

為了實現使用者標籤的整合,使用者 ID 之間的強打通,我們將使用者 ID 標識看成圖的頂點、ID pair 關係看作圖的邊,比如已經識別瀏覽器 Cookie 的使用者使用手機號登陸了公司網站就形成了對應關係。這樣所有使用者 ID 標識就構成了一張大圖,其中每個小的連通子圖 / 連通分支就是一個使用者的全部標識 ID 資訊。

ID-Mapping 資料由圖結構模型構建,圖節點包含 UserKey、Device、IdCard、Phone 等型別,分別表示使用者的業務 ID、裝置 ID、身份證以及電話等資訊。節點之間邊的生成規則是透過解析資料流中包含的節點資訊,以一定的優先順序順序進行節點之間的連線,從而生成節點之間的邊。比如,識別了使用者手機系統的 Android_ID,之後使用者使用郵箱登陸了公司 App,在系統中找到了業務線 UID 就形成了和關係的 ID pair,然後系統根據節點型別進行優先順序排序,生成 Android_ID、mail、UID 的關係圖。資料圖結構模型如下圖所示:

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

Gephi

1.0 版本資料處理流程效能瓶頸

1.0 版本資料處理流程在系統初期較好地滿足了我們的日常需求,但隨著資料量的增長,該方案遇到了一些效能瓶頸:

  1. 首先,這版的資料處理使用了自研的 Java 程式來實現。隨著資料量上漲,自研 JAVA 程式由於資料量暴增導致 JVM 記憶體大小不可控,同時它的維護成本很高,因此我們決定在新版本中將處理邏輯全部遷移至 Flink 中。
  2. 其次,在生成使用者標籤過程中,ID-Mapping 出現很多大的連通子圖(如下圖所示)。這通常是因為使用者的行為資料比較隨機離散,導致部分節點間連線混亂。這不僅增加了資料的維護難度,也導致部分資料被“汙染”。另外這類異常大的子圖會嚴重降低 JanusGraph 與 HBase 的查詢效能。

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

Gephi

  1. 最後,該版方案中資料經 Protocol Buffer(PB)序列化之後存入 HBase,這會導致合併 / 更新使用者畫像標籤碎片的次數過多,使得一個標籤需要讀取多次 JanusGraph 與 HBase,這無疑會加重 HBase 讀取壓力。此外,由於資料經過了 PB 序列化,使得其原始儲存格式不可讀,增加了排查問題的難度。

鑑於這些問題,我們提出了 2.0 版本的解決方案。在 2.0 版本中,我們透過利用 HBase 列式儲存、修改圖資料結構等最佳化方案嘗試解決以上三個問題。

2.0 版資料處理流程

版本流程最佳化點

如下圖所示,2.0 版本資料處理流程大部分承襲了 1.0 版本。新版本資料處理流程在以下幾個方面做了最佳化:

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

2.0 版本資料處理流程

  1. 歷史資料的離線補錄方式由 JAVA 服務變更為使用 Flink 實現。
  2. 最佳化使用者畫像圖資料結構模型,主要是對邊的連線方式進行了修改。之前我們會判斷節點的型別並根據預設的優先順序順序將多個節點進行連線,新方案則採用以 UserKey 為中心的連線方式。做此修改後,之前的大的連通子圖(圖 6)最佳化為下面的小的連通子圖(圖 8),同時解決了資料汙染問題,保證了資料準確性。另外,1.0 版本中一條資料需要平均讀取十多次 HBase 的情況也得到極大緩解。採用新方案之後平均一條資料只需讀取三次 HBase,從而降低 HBase 六七倍的讀取壓力(此處最佳化是資料計算層最佳化)。

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

Gephi

  1. 舊版本是用 Protocol Buffer 作為使用者畫像資料的儲存物件,生成使用者畫像資料後作為一個列整體存入 HBase。新版本使用 Map 儲存使用者畫像標籤資料,Map 的每對 KV 都是單獨的標籤,KV 在存入 HBase 後也是單獨的列。新版本儲存模式利用 HBase 做列的擴充套件與合併,直接生成完整使用者畫像資料,去掉 Flink 合併 / 更新使用者畫像標籤過程,最佳化資料加工流程。使用此方案後,存入 HBase 的標籤資料具備了即席查詢功能。資料具備即席查詢是指在 HBase 中可用特定條件直接檢視指定標籤資料詳情的功能,它是資料治理可以實現校驗資料質量、資料生命週期、資料安全等功能的基礎條件。
  2. 在資料服務層,我們利用 Flink 批次讀取 HBase 的 Hive 外部表生成使用者質量分等資料,之後將其存入 Phoenix。相比於舊方案中 Spark 全量讀 HBase 導致其讀壓力過大,從而會出現叢集節點當機的問題,新方案能夠有效地降低 HBase 的讀取壓力。經過我們線上驗證,新方案對 HBase 的讀負載下降了數十倍(此處最佳化與 2 最佳化不同,屬於服務層最佳化)。

四、問題

目前,線上部署的使用者畫像系統中的資料絕大部分是來自於 Kafka 的實時資料。隨著資料量越來越多,系統的壓力也越來越大,以至於出現了 Flink 背壓與 Checkpoint 超時等問題,導致 Flink 提交 Kafka 位移失敗,從而影響了資料一致性。這些線上出現的問題讓我們開始關注 Flink 的可靠性、穩定性以及效能。針對這些問題,我們進行了詳細的分析並結合自身的業務特點,探索並實踐出了一些相應的解決方案。

CheckPointing 流程分析與效能最佳化方案

CheckPointing 流程分析

下圖展示了 Flink 中 checkpointing 執行流程圖:

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

Flink 中 checkpointing 執行流程

  1. Coordinator 向所有 Source 節點發出 Barrier。
  2. Task 從輸入中收到所有 Barrier 後,將自己的狀態寫入持久化儲存中,並向自己的下游繼續傳遞 Barrier。
  3. 當 Task 完成狀態持久化之後將儲存後的狀態地址通知到 Coordinator。
  4. 當 Coordinator 彙總所有 Task 的狀態,並將這些資料的存放路徑寫入持久化儲存中,完成 CheckPointing。

效能最佳化方案

透過以上流程分析,我們透過三種方式來提高 Checkpointing 效能。這些方案分別是:

  1. 選擇合適的 Checkpoint 儲存方式
  2. 合理增加運算元(Task)並行度
  3. 縮短運算元鏈(Operator Chains)長度

選擇合適的 Checkpoint 儲存方式

CheckPoint 儲存方式有 MemoryStateBackend、FsStateBackend 和 RocksDBStateBackend。由官方文件可知,不同 StateBackend 之間的效能以及安全性是有很大差異的。通常情況下,MemoryStateBackend 適合應用於測試環境,線上環境則最好選擇 RocksDBStateBackend。

這有兩個原因:首先,RocksDBStateBackend 是外部儲存,其他兩種 Checkpoint 儲存方式都是 JVM 堆儲存。受限於 JVM 堆記憶體的大小,Checkpoint 狀態大小以及安全性可能會受到一定的制約;其次,RocksDBStateBackend 支援增量檢查點。增量檢查點機制(Incremental Checkpoints)僅僅記錄對先前完成的檢查點的更改,而不是生成完整的狀態。與完整檢查點相比,增量檢查點可以顯著縮短 checkpointing 時間,但代價是需要更長的恢復時間。

合理增加運算元(Task)並行度

Checkpointing 需要對每個 Task 進行資料狀態採集。單個 Task 狀態資料越多則 Checkpointing 越慢。所以我們可以透過增加 Task 並行度,減少單個 Task 狀態資料的數量來達到縮短 CheckPointing 時間的效果。

縮短運算元鏈(Operator Chains)長度

Flink 運算元鏈(Operator Chains)越長,Task 也會越多,相應的狀態資料也就更多,Checkpointing 也會越慢。透過縮短運算元鏈長度,可以減少 Task 數量,從而減少系統中的狀態資料總量,間接的達到最佳化 Checkpointing 的目的。下面展示了 Flink 運算元鏈的合併規則:

  1. 上下游的並行度一致
  2. 下游節點的入度為 1
  3. 上下游節點都在同一個 Slot Group 中
  4. 下游節點的 Chain 策略為 ALWAYS
  5. 上游節點的 Chain 策略為 ALWAYS 或 HEAD
  6. 兩個節點間資料分割槽方式是 Forward
  7. 使用者沒有禁用 Chain

基於以上這些規則,我們在程式碼層面上合併了相關度較大的一些 Task,使得平均的操作運算元鏈長度至少縮短了 60%~70%。

Flink 背壓產生過程分析及解決方案

背壓產生過程分析

在 Flink 執行過程中,每一個操作運算元都會消費一箇中間 / 過渡狀態的流,並對它們進行轉換,然後生產一個新的流。這種機制可以類比為:Flink 使用阻塞佇列作為有界的緩衝區。跟 Java 裡阻塞佇列一樣,一旦佇列達到容量上限,處理速度較慢的消費者會阻塞生產者向佇列傳送新的訊息或事件。下圖展示了 Flink 中兩個操作運算元之間的資料傳輸以及如何感知到背壓的:

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

首先,Source 中的事件進入 Flink 並被操作運算元 1 處理且被序列化到 Buffer 中,然後操作運算元 2 從這個 Buffer 中讀出該事件。當操作運算元 2 處理能力不足的時候,操作運算元 1 中的資料便無法放入 Buffer,從而形成背壓。背壓出現的原因可能有以下兩點:

  1. 下游運算元處理能力不足;
  2. 資料發生了傾斜。

背壓解決方案

實踐中我們透過以下方式解決背壓問題。首先,縮短運算元鏈會合理的合併運算元,節省出資源。其次縮短運算元鏈也會減少 Task(執行緒)之間的切換、訊息的序列化 / 反序列化以及資料在緩衝區的交換次數,進而提高系統的整體吞吐量。最後,根據資料特性將不需要或者暫不需要的資料進行過濾,然後根據業務需求將資料分別處理,比如有些資料來源需要實時的處理,有些資料是可以延遲的,最後透過使用 keyBy 關鍵字,控制 Flink 時間視窗大小,在上游運算元處理邏輯中儘量合併更多資料來達到降低下游運算元的處理壓力。

最佳化結果

經過以上最佳化,在每天億級資料量下,使用者畫像可以做到實時資訊實時處理並無持續背壓,Checkpointing 平均時長穩定在 1 秒以內。

五、未來工作的思考和展望

端到端的實時流處理

目前使用者畫像部分資料都是從 Hive 資料倉儲拿到的,資料倉儲本身是 T+1 模式,資料延時性較大,所以為了提高資料實時性,端到端的實時流處理很有必要。

端到端是指一端採集原始資料,另一端以報表 / 標籤 / 介面的方式對這些對數進行呈現與應用,連線兩端的是中間實時流。在後續的工作中,我們計劃將現有的非實時資料來源全部切換到實時資料來源,統一經過 Kafka 和 Flink 處理後再匯入到 Phoenix/JanusGraph/HBase。強制所有資料來源資料進入 Kafka 的一個好處在於它能夠提高整體流程的穩定性與可用性:首先 Kafka 作為下游系統的緩衝,可以避免下游系統的異常影響實時流的計算,起到“削峰填谷”的作用;其次,Flink 自 1.4 版本開始正式支援與 Kafka 的端到端精確一次處理語義,在一致性方面上更有保證。

日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐

原文連結

本文為阿里雲原創內容,未經允許不得轉載。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69949601/viewspace-2671254/,如需轉載,請註明出處,否則將追究法律責任。

相關文章