伍翀 :大資料實時計算Flink SQL解密

趙鈺瑩發表於2018-09-06

【IT168 專稿】本文根據伍翀老師在2018年5月12日【第九屆中國資料庫技術大會】現場演講內容整理而成。

講師簡介: 

 

伍翀,阿里巴巴高階研發工程師,花名“雲邪”,阿里巴巴計算平臺事業部高階研發工程師,Apache Flink Committer。北京理工大學碩士畢業,2015 年加入阿里巴巴,參與阿里巴巴實時計算引擎 JStorm 的開發與設計。從 2016 年開始從事阿里新一代實時計算引擎 Blink SQL 的開發與優化。現在專注的方向主要是分散式處理和實時計算,熱愛開源,熱愛分享。

摘要:

SQL是資料處理中使用最廣泛的語言。它允許使用者簡明扼要地宣告他們的業務邏輯。大資料批計算使用SQL很常見,但是支援SQL的實時計算並不多。Apache Flink是一款同時支援批和流計算的引擎,Flink SQL的實現完全遵循ANSI SQL標準,這是它和其他流處理框架(例如Kafka和Spark)在DSL上的一個重要的不同。阿里巴巴是Flink SQL最大的貢獻者,Flink開源社群的一半以上的SQL功能都是阿里工程師開發的。阿里內部絕大部分的流計算業務也都在使用Flink SQL編寫。本次演講我們將介紹Flink SQL的設計原理以及分享在阿里大規模使用中收穫的經驗。

分享大綱:

1、Background

2、Flink SQL基本概念

3、Flink SQL核心功能

4、Flink SQL優化

5、阿里雲流計算產品

正文:

阿里巴巴自2015年開始調研開源流計算引擎,最終決定基於Flink打造新一代計算引擎,針對Flink存在的不足進行優化和改進,並將最終程式碼貢獻給開源社群。目前為止,我們已經向社群貢獻了數百個Commiter。阿里巴巴將該專案命名為Blink,主要由Blink Runtime與Flink SQL組成。Blink Runtime是阿里巴巴內部高度定製化的計算核心,Flink SQL則是面向使用者的API層,我們完善了部分功能,比如Agg、Join、Windows處理等。今年,我們已經全部跑通TPCH 及TPC-DS的Query,熟悉資料庫的人都知道,這代表著整個資料庫或引擎是一個基本功能完備的產品。

接下來主要介紹Flink SQL的基本概念及使用。傳統的流式計算引擎,比如Storm、Spark Streaming都會提供一些function或者datastream API,使用者通過Java或Scala寫業務邏輯,這種方式雖然靈活,但有一些不足,比如具備一定門檻且調優較難,隨著版本的不斷更新,API也出現了很多不相容的地方。

我們一直在思考最適合流計算處理的API,毫無疑問,SQL已經成為大資料領域通用且成熟的語言,因此我們的Flink和Blink均基於此,之所以選擇將SQL作為核心API,是因為其具有幾個非常重要的特點,一是SQL屬於設定式語言,使用者只要表達清楚需求即可,不需要了解具體做法;二是SQL可優化,內建多種查詢優化器,這些查詢優化器可為SQL翻譯出最優執行計劃;三是SQL易於理解,不同行業和領域的人都懂;四是SQL非常穩定,在資料庫30多年的歷史中,SQL本身變化較少,非常穩定。當我們升級或替換引擎時,使用者是無感知的且完全相容;最後,SQL經過優化可以統一流和批。

過去,我們既需要批模式跑全量資料,也需要流模式實時跑增量資料,因此需要同時維護兩個引擎,並且保持兩份程式碼之間的同步。如果使用SQL,我們便可以一份程式碼同時跑在兩個模式下,但SQL是為傳統批處理設計的,並不能為流處理所用。SQL定義在表上,而不是流上。傳統SQL處理的資料集比較有限,查詢一次只返回一個結果。但是,流處理需要不斷接收資料,不斷對結果進行更新,並且查詢也不會結束,這導致其需要對歷史資料不斷修正。所以,SQL的很多概念無法直接對映到流計算,這就是在流計算上定義SQL的難點。 

 

為了在流計算上定義SQL,我們需要引入幾個概念。既然批處理需要定義SQL表的概念,那在流計算上也需要表的概念,我們需要將傳統靜態表擴充套件成動態表,所謂動態表就是資料會隨時間而不斷變化的表。此時,我們發現流和動態表之間有一種對偶性,也就是說流和動態表可以相互轉換。將流的每條資料插入到資料庫中,就得到了一張表;同時我們可以抽取動態表的changelog還原原始流。

 

從流計算到SQL,我們可以把它看成是連續查詢。連續查詢區別於傳統的批處理查詢,需要源源不斷地接收資料,每收到一條新資料就會更新結果且結果也是一張動態表,那結果的動態表又可以作為下一個查詢的輸入,從而串起整個流計算。

基於上述兩個概念,我們可以在SQL上定義流計算。但是,流計算中的資料需要不斷修正和更新,因此這些資料下發後可能導致最終結果的錯誤,我們需要把這些錯誤資料進行修正,這就涉及到流計算中一個非常重要的概念——Retraction。

 

為了解釋此概念,我們舉一個簡單的例子,上圖所示有一個點選輸入流,它具備兩個欄位:user和url,經過第一個查詢根據使用者進行分組,統計每個使用者的點選次數;進入第二個查詢,根據點選次數進行分組,統計每個次數的具體點選人數。最終,我們會收到兩條記錄,點選次數所對應的人數。從結果明顯可以看出計算有誤,Mary的資料並沒有合併計數,這就需要引入修正的概念。 

 

如上圖所示,經過修正之後,經過第二個查詢時,Mary的總查詢次數會被合併計算,Mary 1的結果會被告知撤回,從而輸出正確的結果,這就是引入Retraction的作用。在整個過程中,是否觸發Retraction以及傳送方式均由優化器決定,使用者對整個過程是無感知的。

在此基礎上,我們發現世界不需要所謂的Stream SQL語法,標準的ANSI SQL就可用來定義流計算,Flink SQL就是標準的ANSI SQL語法。其部分核心功能如下:DDL用來定義資料來源表、資料結構表;UDF、UDTF、UDAF使用者自定義函式,可以定製化使用者複雜的業務需求;JOIN是一個比較複雜的功能,包括流與流之間的Join,流與表之間的Join以及Windows Join等;聚合功能包括類似Group AGG,Windoes Agg以及Over Agg等。 

 

接下來我會結合例項對核心功能進行介紹。首先是裝載資料,需要create table語法。如上圖所示,我們先定義一張clicks表,然後定義表的schema、user、cTime以及url,with裡是表的一系列屬性,它是一個來自kafka的日誌表,我們可以用SELECT * FROM clicks查詢轉載表裡面的資料。

 

如果要將上述查詢資料寫到某個表中,我們需要用create table定義結果表,語法同上,建立一張 last_clicks 結果表,主鍵是user,通過INSERT INTO 語法將上述查詢資料插入Mysql表中。

 

如果想把中間處理結果同時寫入多個儲存,比如把資料處理結果同時寫到Mysql和HBase,如上使用CREATE VIEW 定義一個來自淘寶的點選記錄,同時連續寫多個INSERT INTO到Mysql和HBase。

 

接下來是Group Aggregate,也就是無限流量聚合。所謂無限流量聚合指從歷史開始到現在的所有使用者點選資料,如上查詢展示的是根據使用者分組,然後統計點選次數。如果來了一條Mary1的資料,我們就先插入該資料,後續如果Mary再次進行點選,我們就在原資料基礎上進行修改更新,以此類推。

 

Window Aggregate是定義在視窗上的聚合,有別於上述無限流聚合,它的原理是是每個視窗對應輸出一個結果,比如每小時每個使用者的點選次數,需要在group by的結果上加上endT資料,也就是視窗標識。 

 

接下來介紹雙流join,目前我們支援INNER, LEFT, RIGHT, FULL, SEMI, ANTI等Join型別,舉例說明雙流Join的主要使用場景,比如把主流打成寬表,並補上額外欄位等。如上圖所示,我們需要將訂單和物流表資訊進行Join操作,在Join的物理實現上會有兩份狀態,用來儲存兩條流到目前為止收到的所有歷史資料,淘汰機制時間設定為一天半一次。兩者中任何一方資訊延遲都會先在表中等待,直到同一個訂單的資訊與物流關聯之後才會通過Join輸出。 

 

維表Join與雙流Join類似,目前支援INNER, LEFT兩種交易型別。維表Join的使用同樣為補全主流,但想補全的欄位在另一維表中。如上圖所示,使用時首先需要通過CREATE TABLE 語法定義一張維表,此處定義的是 Products 表,儲存與產品相關資訊,查詢同樣使用Join語法。Order與Products表通過Products ID實現Join。關鍵字PERIOD FOR SYSTEM_TIME 是 SQL  2.11標準裡的語法,意思是當前關聯的Products是當前時刻的資訊,關聯之後不再更新資訊。上圖右側展示的是維表Join物理執行的概念。我們可以根據Order去Products資料庫裡查詢資訊,最終Products維表返回關聯資訊。

 

核心功能如上所述,接下來主要聊優化。維表中,訂單O1查詢時是堵塞等待IO的狀態,此時無論如何調優效能,吞吐量和CPU使用率都上不去,因此我們引入非同步IO功能。

 

如上左半部分為未引入非同步IO時的狀態,如上右半部分為引入後,此時若發起A請求,不需等待IO就可立刻發起BCD查詢請求,然後非同步等待返回結果。返回ABCD以後再管理輸出,極大地提高了整體效能。

 

如上,非同步IO使用時與維表Join只有一行配置改動,對於使用者來說,這個使用是非常簡便的。

 

第二個優化是大資料中的常見場景——資料傾斜。如上為改進之前,紅色聚合節點出現資料積壓現象,而紫色節點相對較空。

 

如果持續一段時間,紅色聚合節點就會被打滿,從而變為熱點,所有上游map節點就會反壓,停止處理資料進入等待狀態,而下游的紫色節點基本處於空閒狀態。

 

 

我們引入Local-Global 聚合優化。左圖是未優化拓撲圖,右邊是引入Local-Global優化後的圖,我們在Map後引入Local Agg節點,Map與Local Agg是鏈在一起的一個執行緒,之間的資料傳輸沒有任何網路開銷。Local Agg可以將收到的資料按照 key進行預聚合,然後將結果按照 key分發給下游Global Agg進行彙總。

假如每個Map的 TPS 是每秒1萬的資料量,全域性就2個 key:紅色和紫色。如果 Local Agg聚合的間隔是每秒鐘一次,那麼每個Local Agg能將1萬條資料預聚合成最多2條(全域性共2個 key)。那麼Global Agg每秒鐘最多收到只會三條訊息,能有效降低Global Agg 的熱點。優化後,我們對此進行效能測試,發現Local-Global 可以帶來超過20倍的效能提升。因此,整個方案是十分有效的。

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

相關文章