一個
導讀
本文介紹58信安基于Flink實現低代碼實時數倉構建系統,我們將數倉構建這一過程進行抽象,通過工程化的思想去解決,將固有領域問題交給系統,讓開發人員關注數據本身,解放人力縮短數倉構建周期。
2
背景
隨著數據驅動業務需求的不斷增加,數據倉庫的建設越來越頻繁。開發人員在數據倉庫的構建過程中(嵌入、數據接收、數據補全、數據清洗、數據寫入存儲介質)做大量重復性的工作。同時,對于實時數據倉庫的構建,他們需要一定的專業技能,比如如何使用Flink等框架做過濾、轉換、聚合等。對于后端業務團隊來說,學習成本。為了解決這些問題,低代碼的建庫系統應運而生,用工程化的思路解決,把固有的領域問題交給系統,讓開發者關注數據本身,解放人力,縮短建庫周期。
三
整體架構
參考數據倉庫的分層,我們將系統分為ODS、DWD、DWS、ADS四層,這四層的運行由系統的核心“執行引擎”驅動。通過這四層對數據進行逐層處理,最終為上游業務提供指標監控、數據行情、Ad-hoc查詢等應用支持。
ODS層:記錄了最原始的數據,包括用戶行為日志、業務日志、系統日志等。
DWD層:ODS層的數據通過數據清理、維度補全、拆分合并等,組織成一個業務列表或一個寬泛的主題明細列表。
DWS層:DWD層的數據按照各種維度進行輕粒度的聚合和統計,用于為上層應用場景提供統計便利。
ADS層:DWD或DWS層的數據通過各種數據存儲介質實時存儲,為各種業務場景提供支持,如指數監測、數據行情、即席查詢等。
四
“零”開發的設計實現
為了解放開發人力,我們的設計理念是“低代碼”,整個業務流程由定制的專業領域語言(DSL)結合規則引擎驅動。用戶只需要配置DSL,這樣就避免了代碼開發。技術實現的重點是如何用DSL表達整個業務流程,如何設計這個規則引擎來執行DSL,最終完成流程。
DSL的設計:
這個業務核心流程的本質就是使用Flink(流處理系統)進行數據TEL的過程。我們把這個過程簡稱為源(輸入)-轉換(轉化)-聚合(聚合)-匯(輸出)這些STAS,最后把所有數據流組織成一個有向圖:
DSL本質上就是表達這個有向圖,下面的語法例子就是描述某個流。數據源是kafka,經過轉換聚合后輸出到一個hive目錄:
自定義規則引擎的設計:
目前業界有很多成熟的規則引擎,如drools、aviator、mvel、easyrules等。盡管它們是水平的
比會有規則支持力度和性能的差異,但是他們基于通用規則的本質就決定了存在語法復雜和執行效率的問題。由于我們面對的問題域相對固定,可以考慮對數倉構建這一業務領域進行抽象,進而完成貼合業務的自定義的規則引擎,這樣有形成針對性的規則語法復雜度會相應降低,同時也避免了語法執行效率低下的問題。
自定義規則引擎在流程處理上只需要解析多個DSL所構成的有向圖(上面介紹STAS有向圖),而在規則處理方面就是在執行STAS的規則,其中“輸入和輸出”都是數據源和存儲介質的問題,本身就是有限集,而“聚合”本身Flink就有SQL支持,所以我們只需要集中處理“數據轉換”的問題,數據轉換規劃可以說是無限集,但是在這個無限集中會有一個2/8原則,也就是說20%的規則可以滿足80%的需求,例如數據轉換經常面對的“屬性命名標準化”,“數據類型轉換”,“條件判斷”等,而剩余的零散的規則可以采用定制化開發的方式。整個規則引擎的技術邏輯也就是上述描述的部分,如下圖(藍色為引擎,綠色為數據):
最終我們將DSL數據轉化成頁面的配置化,引導使用者快速構建實時數倉,總體流程如下:
5
插件化設計實現高擴展
基于DSL的技術實現代替了硬編碼,而DSL代替硬編碼的程度,就取決于其規則語法的豐富程度,當語法開始膨脹的時候,如何開發和維護語法,如何讓開發語法的人只關注語法本身,即使不了解系統本身也可以開發語法,我們的方案就是插件化,一切皆插件,如下圖(紅色框部分,每一個都是插件):插件的本質就是自定義的Java代碼,同時插件在定義上分為功能型和語法型兩種,功能型插件描述這個數據是用什么處理的,以$P開頭,語法型插件描述著這個數據是怎么處理的,以#開頭。
插件的語法定義如下:
插件的代碼定義如下:
按照2/8原則系統會內置一部分插件作為最基礎的能力,剩余的部分通過下載Jar包的方式進行載入,插件在系統中的執行過程為“下載”、“加載”、“命中”、“運行”這四步(系統內置的插件不需要下載),插件的總體執行邏輯如下:
6
維表數據補全
維表補全在實時數倉構建中是一個必不可少的過程,一般分為業務關聯補全和數據字典補全,我們結合實際需求提供兩種補全方案:
(1)基于緩存同步:
在程序啟動時,將mysql、hbase、wtable等外部存儲資源加載到內存,在transform過程中查詢對應內存數據做維表補全,這種方案由于受限于內存和資源的更新頻率,比較適合處理數據量有限并且變化頻率不高的數據,如某某基本信息、數據字典等。我們提供了對應的補全插件,以mysql的維表補全為例:
(2)基于實時查詢:
在transform過程中查詢對應數據庫或者服務接口,這種方案不限于內存且在實時性上有所保證,避免了基于緩存的問題,但是在執行效率和并發性會受限,比較適合處理數據變化頻率高但是實時流本身體量不大的情況。對于實時查詢的維表補全方式,由于查詢的邏輯差異較大,這時候可以由接入方來編寫自定義插件完成
7
多流合并
實時數倉構建過程中會經常遇到流合并問題,即將兩個流合并成一個流,Flink在流合并上提供兩種模式,一種是基于時間窗口(滑動,滾動,會話)的join ,一種是基于無時間窗口的interval join。
結合實際業務場景,真實的數據往往會出現跨時間窗口分布的情況,舉個例子:將用戶的帖子瀏覽數據流和帖子點贊數據流合并,由于瀏覽和點贊有個天然的時間跨度,這種情況下,無論選擇什么類型和多長跨度的時間窗口,都會有一部分數據會跨窗口,此時數據就無法合并,為了解決這個問題 ,我們利用官方提供的interval join做無時間窗口合并。
但是需要注意的是,interval join的本質就是流數據的等待,但是等待就意味著系統會占用內存甚至存儲資源hold住當前數據,在面對具體需求的時候,使用者要根據當前的數據量級,可接受的延時等因素綜合來決策。
8
聚合運算
聚合運算用于DWS層的數倉構建,將DWD層經過輕粒度的聚合,可以為上游統計類應用減輕壓力,也可以作為基礎數據支撐報表、大盤等應用場景。我們提供兩種方式構建DWS層數據,對于簡單的聚合運算,我們提供基于原始Flink語法的聚合插件進行處理,對于復雜的聚合運算,我們提供基于Flink SQL的聚合插件進行處理。
9
數倉構建
構建系統支持hdfs,clickhouse,mysql 等多種存儲介質的寫入,用于支持應用層各種實時或者離線的統計需求,以下介紹兩種常用的存儲介質
sink to hdfs:
hdfs是數倉構建過程中的核心存儲介質,為了支撐快速檢索,我們將hdfs按照時間進行分區,分區粒度為小時級partition - {day} - {hour} ,在時間模式的選擇上我們采用flink 的 eventtime,這么選擇一是考慮到eventtime可以真實反映數據的生成時間,更貼合業務場景,二是考慮到當實時流處理出現問題的時候,可以通過離線日志按照eventtime進行重塑。
sink to clickhouse:
clickhouse作為OLAP數據庫 , 很好的支撐了海量數據實時深度挖掘的應用場景,我們對clickhouse的sink 操作采用集團支持的解決方案,即將數據源推到kafka中,由集團訂閱kafka來構建clickhouse數據
10
階段性成果及展望
目前我們的低代碼實時數倉構建系統支撐了部門內多個業務,完成日均30億+的數據處理工作,數倉構建成本從2~3人日降低到小時級,極大的縮短了數倉構建周期,支持了上游應用的快速迭代。
面向未來,我們期望本套系統起始于數倉構建但并不終止于數倉構建,基于實時流或是批流一體化處理的應用場景非常廣泛 ,例如實時監控預警、特征工程、算法模型訓練等等,這些都可以去探索。
參考文獻:
https://flink.apache.org/
作者簡介:
邢而康:58同城-信息安全部-后端資深開發工程師
陸航:58同城-信息安全部-后端高級開發工程師