【實踐案例分享】菜鳥實時數倉技術架構演進


分享嘉賓:賈元喬 菜鳥 高級數據技術專家

編輯整理:夏飛飛

內容來源:Flink Forward ASIA

出品平臺:DataFunTalk

導讀:在開源盛世的今天,實時數倉的建設已經有了較爲成熟的方案,技術選型上也都各有優劣。菜鳥作爲物流供應鏈的主力軍,時效要求已經成爲了核心競爭力,離線數倉已不能滿足發展的需要,在日益增長的訂單和時效挑戰下,菜鳥技術架構也在不斷髮展和完善,如何更準更高效的完成開發和維護,變得格外重要。本文將爲大家分享菜鳥技術團隊在建設實時數倉技術架構中的一些經驗和探索,希望能給大家帶來啓發。

本文主要包括以下內容:

  • 以前的實時數據技術架構

  • 數據模型、計算引擎、數據服務的升級

  • 其他技術工具的探索和創新

  • 未來發展與思考

01

以前的實時數據技術架構

數據模型:

  • 業務線內部模型層次混亂,數據使用成本特別高

  • 需求驅動的煙囪式開發,完全沒有複用的可能性,計算成本居高不下

  • 各業務線橫向或者縱向的交叉,導致開發過程中數據一致性偏差較大

  • 縱向的數據模型,導致 BI 使用時比較困難

實時計算

  • 我們之前使用阿里雲的 JStorm 和 Spark Streaming 進行開發,這兩部分能滿足大部分的實時數據開發,但是應用到物流供應鏈場景當中,實現起來並不簡單,甚至無法實現

  • 很難同時兼顧功能、性能、穩定性以及快速故障恢復能力

數據服務

  • 開發過程中,實時數據下沉到 MySQL,HBase 等數據庫中,查詢和保障方面不靈活

  • BI 權限控制和全鏈路保障不可靠

02

數據模型升級

1. 模型分層

參考離線數倉,將實時數據進行分層,第一層是數據採集,將從 MySQL 等數據庫中採集到的數據放到 TT 消息中間件中,然後基於 TT 消息中間件和 HBase 的各種維表關聯產生事實明細寬表,再將生成的數據寫到 TT 消息中間件中。通過訂閱這個消息產生輕度彙總層和高度彙總層兩層。輕度彙總層主要按照多個維度沉澱數據,高度彙總層主要用於大屏場景。

2. 預置分流

左邊公共數據中間層是將所有業務線整合在一起,然後進行分層。右邊的業務數據中間層是各個業務基於橫向的公共數據中間層的基礎上,去分流自己業務的數據中間層,然後根據這些實時的消息,個性化的產出自己業務的數據中間層。比如不同的訂單,可以橫向分流成進口供應鏈和出口供應鏈。這樣實現了上游只需要一個公共分流作業完成,節約了計算資源。

3. 菜鳥供應鏈實時數據模型

左邊是公共的數據中間層,裏面包含整個大盤的訂單數據,大盤的物流詳情,彙總的公共粒度數據等,在這個基礎之上做了個分流任務,然後從物流訂單,物流詳情等裏面拆分出我們自己個性化的業務,比如國內的供應鏈,進口的供應鏈以及出口的供應鏈等。經過這些步操作之後,可以輕易的區分哪些表是大屏的數據,哪些表是統計分析的數據,在數據易用性方面有了很大的提升。

03

計算引擎的提升

一開始,我們採用了 JStorm 和 Spark Streaming 進行實時開發。這兩個計算引擎在滿足大部分的場景時,沒有特別大的問題,但是在應用到供應鏈或者物流場景時,不是那麼簡單。所以我們在17年切換到了 Flink 上,Flink 提供了一些很實用的功能,而這些功能在一些供應鏈的場景下比較適用。

首先是內部 Flink 支持一套完整的 SQL 寫法,提高了開發效率。第二點是 Flink 內置的基於 State 的 retraction 機制,很好的支持了供應鏈當中的取消訂單,換配等這種操作,而且非常簡單。Flink 的 CEP 功能,方便的實現了超時統計的功能,還有目前正在推的 AtoScaling 方案,比如數據傾斜,資源配置等。另外一點是 Flink 在批流混合問題的處理,有很好的支持。

1. 神奇的 Retraction

左邊有4列數據,第1列數據是物流訂單,第2列的數據是物流訂單的創建時間,第3列數據是這個訂單是不是被取消,第4個數據是計劃配送公司,就是訂單分配給哪個配送公司。這個業務需求看上去非常簡單,其實就是統計每個配送公司計劃履行的有效單量有多少。但是有兩個點需要注意一下,第一點,有一個訂單 LP3,在開始的時候是有效的,然後在最後一條時取消了,就變成了一個無效訂單,但這一條訂單不應該統計在內,因爲業務需求統計的是有效訂單。第二點,配送公司的轉變,LP1 這個訂單在一分鐘時是 tmsA 來配送,後來變成了 tmsB,再過了一段時間,這個訂單又變成 tmsC 配送。如果基於 Storm 增量計算,得出的結果顯然是錯誤的,這時要按照最後一次消息給我們傳過來的數據統計。這樣的場景在 Flink 中是如何實現的呢?Flink 也提供了一些非常好的回撤機制,下面是僞代碼的實現:

第一段代碼使用 Flink 的 last_value 函數,獲取這個訂單最後一個消息非空的值,在這個基礎上進行彙總。一旦 last_value 中字段發生變化,都會觸發撤回機制,得到最後正確的值。

2. 實時超時統計

這個案例發生在菜鳥實際物流場景中,第1個表格是一個日誌的時間,第2個是物流訂單,第3個字段是出庫時間,最後一個字段是攬收時間,現在需要統計的是出庫超6個小時沒有被攬收的單量。這裏涉及到全鏈路時效問題,全鏈路時效指從下單到倉庫發貨到快遞攬收到簽收的整體時間,這個場景放在離線中是非常容易實現的,但是放到實時中來不是很簡單, 比如 LP1 在00:05分的時候沒有攬收,現在如果當下時刻是12點的話,也沒有攬收,理論上應該計算這個訂單,但是沒有攬收就意味着沒有消息進來,沒有消息進來我們又要統計,其實我們用 Flink 是沒法統計的,那這種情況我們怎麼處理呢?我們的解決方案是如果沒有這條消息我們用 Flink 來製造這條消息。這種超時消息統計我們想到了幾種方法:

包括引入消息中間件 ( kafka ) 和 Flink 的 CEP。最終選擇了 Flink 的 Timer Service,因爲這種消息不是特別多,中間件又特別重。而 CEP 會丟掉一些回傳不準確的消息,導致數據計算不準確,針對這些情況,我們在調研之後選擇了 Timer Service,同時我們對它底層的 ProcessElement 和 OnTimer 兩個方法進行了改寫。ProcessElement 告訴 Flink 存儲什麼樣的數據,然後啓動針對每一個超時的事件的 Timer Service。OnTimer 方法會在每個超時的時刻讀這個超時的消息,並把這個超時的消息下發下來。基於下游跟正常流的關聯操作之後就能計算超時消息的單量。僞代碼如下:

先構造一個 process funcation 到 state 存數據,併爲每一個超時的數據註冊一個 Timer Service。然後執行 OnTimer 這個方法,讀取並把這個超時的消息下發下去。

3. 從手動優化到智能優化

關於數據傾斜的問題,左圖顯示在 map 階段 shuffer 之後數據傾斜到了紅色的 Agg 上,這時就出現熱點了,原來我們是對這個 lg_order_code 進行 hash 取值操作,然後再針對散列的結果進行二次的聚合,這樣操作後在一定程度上減輕了數據的傾斜。在最近的 Flink 的版本中已經實現了規避數據傾斜的方法,我們內部的 Blink 版本,有幾個功能去優化熱點的問題,第一個就是 MiniBatch,之前來一條數據,我們就去 State 裏面查詢然後寫入,MiniBatch 的作用是把所有的數據先聚合一次,類似一個微批處理,然後再把這個數據寫到 State 裏面,或者在從 State 裏面查出來,這樣可以大大的減輕對 State 查詢的壓力。第二個辦法就是 LocalGlobal,類似於在 hive 中 map 階段中的 combiner,通過設置這個參數可以在讀的時候先聚合。第三個辦法是 PartialFinal,類似於散列的方式,分兩次聚合,相當於 hive 中兩個入 reduce 操作。通過設置這三個參數,可以在大部分場景規避數據傾斜的問題。

智能化功能支持的另一個場景是資源配置。在進行實時 ETL 過程中,首先要定義 DDL,然後編寫 SQL,之後需要進行資源配置。針對資源配置問題,菜鳥之前的方案是對每一個節點進行配置,包括併發量、是否會涉及消息亂序操作、CPU、內存等,一方面配置過程非常複雜,另一方面無法提前預知某些節點的資源消耗量。Flink 目前提供了較好的優化方案來解決該問題:

  • 大促場景:該場景下,菜鳥會提前預估該場景下的 QPS,會將其配置到作業中並重啓。重啓後 Flink 會自動進行壓測,測試該 QPS 每個節點所需要的資源。

  • 日常場景:日常場景的 QPS 峯值可能遠遠小於大促場景,此時逐一配置 QPS 依然會很複雜。爲此 Flink 提供了 AutoScaling 智能調優的功能,除了可以支持大促場景下提前設置 QPS 並壓測獲取所需資源,還可以根據上游下發的 QPS 的數據自動預估需要的資源。大大簡化了資源配置的複雜度,使得開發人員可以更好地關注業務邏輯本身的開發。

04

數據服務的升級

在開發的過程中常用的數據庫比較少,因此統一數據庫連接標準是有必要的。我們開發的叫天工,它可以提供整個數據庫統一的接入標準,提供統一的權限控制,提供統一的全鏈路的保障。這個中間件將 SQL 作爲 DSL,並且提供一些標準化的 HSF 的服務方式。作爲菜鳥數據服務的踐行者,天工也提供了一些非常貼近業務的,非常實用的功能,下面是幾個案例。

1. NoSQL To TgSQL

對於 HBase 這種 NoSQL 數據庫,BI 或者運營來說用代碼來實現需求是比較困難的,所以開發天工的時候第一件事情就是把一些 NoSQL 轉化成天工 SQL,包括我前面說的一個人員的錶轉化成一個二維表,這裏是邏輯的轉換,不是實際物理上的轉化,大家通過運行這個 SQL,後臺的中間件會自動轉化成查詢的語言,去查詢後臺的數據。

2. 跨源數據轉化

在開發數據產品的過程中,我們發現實時跟離線有時候分不開,比如有一個比較大的場景,需要統計實時 KPI 的完成率,它的分子是實際單量,分母是已經計劃好的單量,數據源是來自兩個部分,第一個部分來自已經做好的 KPI 的一個表,然後第二部分是一個實時計算出來的表。對於這種場景,之前我們是用 Java 去計算這兩部分數據,然後在前端去運算,比較麻煩。現在通過天工 SQL 直接取這兩部分數據關聯,做到跨源數據的操作。 

3. 服務保障升級

原來在整個服務的保障比較缺失,比如某個數據服務出了問題,我們直到運營反饋的時候才發現有問題,或者數據量比較大的時候,要去做限流和主備切換。所以在數據服務的這一層中也把數據服務保障加到了天工的中間件裏面。還有主備雙活,將流量大的放在主庫,流量適中的放在備庫上。針對一些複雜的查詢,在執行的時候很慢,我們會自動識別這些慢查詢,然後進行阻斷,等待資源充足後再執行,當然,也可通過添加白名單用戶進行限流。上面這些功能在天工裏面都有實現。

05

其他技術工具的探索和創新

除了前面講的,我們在技術工具上也和阿里雲計算平臺的事業部進行了探索。每年遇到大促都要進行壓測,大家要去啓動數據,模擬大促流量,看看我們的實時作業能不能滿足預期,比如有延遲,或者 QPS 過高,在原來我們會重啓作業,然後把 source 和 sink 改成壓測 source 和 sink,操作起來非常的麻煩。後來我們做了一個實時的壓測工具,可以做到一鍵啓動所有重要的壓測任務,並且會生成壓測報告。我們只需要看壓測本報告有沒有滿足我們的預期就行。基於 Flink 之後,我們開始做基於作業進度的監控,比如延遲監控、checkpoint 的監控、TPS 的預警等。

06

未來發展與思考

菜鳥目前在實時數倉方面更多的是基於 Flink 進行一系列功能的開發,未來的發展方向計劃向批流混合以及 AI 方向演進。

Flink 提供了 batch 功能。菜鳥很多中小型的表分析不再導入到 Hbase 中, 而是在定義 source 的時候直接將 MaxCompute 的離線維表讀到內存中,直接去做關聯,如此一來很多操作不需要再進行數據同步的工作。

針對一些物流的場景。如果鏈路比較長,尤其是雙十一支付的訂單,在十一月十七號可能還存在未簽收的情況,這時候如果發現作業中有一個錯誤,如果重啓的話,作業的 State 將會丟失,再加之整個上游的 source 在 TT 中只允許保存三天,使得該問題的解決變得更加困難。

  • 菜鳥之後發現 Flink 提供的 batch 功能可以很好地解決該問題,具體來講是定義 TT 的 source,作爲三天的實時場景的應用,TT 數據寫到離線數據庫進行歷史數據備份,如果存在重啓的情況,會讀取並整合離線的數據,即使 Flink 的 state 丟失,因爲離線數據的加入,也會生成新的 state,從而不必擔心雙十一的訂單如果在十七號簽收之前重啓導致無法獲取十一號的訂單信息。

  • 當然,在上述問題的解決上,菜鳥也踩了很多的小坑。其中的一個是整合實時數據和離線數據的時候,數據亂序的問題。菜鳥實現了一系列的 UDF 來應對該問題,比如實時數據和離線數據的讀取優先級設置。

針對日誌型的業務場景。比如曝光、網站流量等,其一條日誌下來後,基本不會再發生變化。菜鳥目前在考慮將所有解析的工作交給 Flink 來處理,然後再寫入到 batch 中,從而無需在 MaxCompute 的 ODPS 中進行批處理的操作。

在智能化方面。前面提到的數據傾斜隱患的規避、資源的優化等,都用到了 Flink 提供的智能化功能。

  • 菜鳥也期望在實時 ETL 過程中的一些場景中,比如去重,也使用 Flink 相應的智能化解決方案來進行優化。

  • 此外,在數據服務保障上,如主備切換等,目前仍然依賴人工對數據庫進行監控,菜鳥也期望 Flink 之後能提供全鏈路實時保障的策略。

  • 最後是業務場景的智能化,阿里 Alink 對於業務智能化的支持也是之後探索的方向。

熱門文章

直戳淚點!數據從業者權威嘲諷指南!

數據分析師做成了提數工程師,該如何破局?

全棧型VS專精型,團隊到底需要什麼樣的人?

數據驅動業務,比技術更重要的是思維的轉變

最近面了十多個數據分析師,聊一聊我發現的一些問題

一個在看,一段時光????

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章