Flink入坑指南第三章:第一個作業
摘要: Flink入坑指南系列文章,從實際例子入手,一步步引導使用者零基礎入門實時計算/Flink,併成長為使用Flink的高階使用者。本文屬個人原創,僅做技術交流之用,筆者才疏學淺,如有錯誤,歡迎指正。轉載請註明出處,侵權必究。
Flink介面
Flink支援三種介面,也就是三種寫作業的方式:
- SQL:實時計算產品與開源Flink相比,提供更全面的語法支援,目前95%的使用者都是使用SQL解決其問題
- TableAPI:表達能力與SQL相同
- DataStream API:底層API,需要一定基礎
本系列文章會從流式SQL開始介紹。重點是幫助使用者理解批和流SQL的區別,使使用者能快速上手,在Flink上寫出正確的流SQL。
不會介紹實時計算/Flink SQL的語法細節,關於SQL語法或各內建函式的用法,請參考文件:幫助手冊
有志於瞭解FlinkSQL實現原理或研究Flink程式碼的同學,可以參考《Apache Flink 漫談系列 – SQL概覽》
明確需求
接上一章內容,本章計算一個指標:
- 當天0點開始,全網的成交量
使用系統:
- 上游(SOURCE):Kafka
- 下游(SINK):MySQL
開始寫第一個作業
原始資料
ctime | category_id | shop_id | item_id | price |
---|---|---|---|---|
2018-12-04 15:44:54 | cat_01 | shop_01 | item_01 | 10 |
2018-12-04 15:45:46 | cat_02 | shop_02 | item_02 | 11.1 |
2018-12-04 15:46:11 | cat_01 | shop_03 | item_03 | 12.4 |
主要邏輯
源表:
-- 源表DDL
create table src(
ctime timestamp, -- 交易時間戳
category_id varchar, -- 類目id
shop_id varchar, -- 店鋪id
item_id varchar, -- 商品id
price double -- 價格
)
-- 結果表DDL
create table sink(
cdate date, -- 日期
gmv_daily double -- 從零點開始,每天的全網成交金額
)
批SQL寫法
一般批的寫法:
SELECT
date_format(ctime, `%Y%m%d`) as cdate, -- 將資料從時間戳格式(2018-12-04 15:44:54),轉換為date格式(20181204)
SUM(price) AS gmv_daily
FROM src
GROUP BY date_format(ctime, `%Y%m%d`) ; --按照天做聚合
結果:
cdate | gmv_daily |
---|---|
20181204 | 33.5 |
特點:
- 每次執行前,資料庫中都儲存了當天已經入庫的全量資料
- 每次執行SQL,都會拿到__一個__返回值,然後SQL執行結束
Flink SQL寫法
SELECT
date_format(ctime, `%Y%m%d`) as cdate, -- 將資料從時間戳格式(2018-12-04 15:44:54),轉換為date格式(20181204)
SUM(price) AS gmv_daily
FROM src
GROUP BY date_format(ctime, `%Y%m%d`) ; --按照天做聚合
特點:
- Flink SQL是個常駐程式,一個SQL檔案,就對應與一個Flink作業。如果使用者不殺掉這個作業,這個SQL就會一直存在
- 每來一條資料,這個Flink作業都會輸出一個值。如果MySQL結果表中沒有加主鍵,那看到的結果如下:
cdate | gmv_daily |
---|---|
20181204 | 10.0 |
20181204 | 21.1 |
20181204 | 33.5 |
如果把MySQL結果表中的cdate欄位作為主鍵,那麼每來一條資料,這個Flink作業都會輸出一個值,三條資料的主鍵相同,因此會覆蓋之前的結果,等三條資料都經過Flink計算後,得到的結果如下:
cdate | gmv_daily |
---|---|
20181204 | 33.5 |
原理介紹
這個例子中,批和流的SQL相同,從最終結果看也相同。但是批引擎(比如MySQL/Hive等)的執行模式,和流引擎(如Flink)是完全不同的。這就導致同一個SQL在處理資料的行為上,會有很多區別。如果要深入使用Flink SQL,並且保證結果的正確性,成為Flink SQL調優專家,就需要對Flink底層實現有一定的瞭解。接下來每一章的例子之後,都會介紹一下本章所用的基礎原理。但不會講實現細節,需要了解實現細節的同學,可以follow flink原始碼。
1. 從批和流SQL的行為開始 – 持續查詢
從直觀上看,批和流SQL行為非常不同:
- 批SQL每執行一次,只返回一次結果,針對計算時刻資料庫中資料的快照做計算
- 流SQL只要啟動,就會一直執行,在有資料的情況下會不停的產生結果
這裡涉及到的流計算中新增的一個非常重要且基礎的概念持續查詢。簡單理解,持續查詢的特點,Flink 作業會一直執行其SQL的邏輯,每到一條新資料,都會觸發下游計算,從而源源不斷的產生輸出。
該SQL中有兩個關鍵操作:
- Group By:分組操作,在批SQL和流SQL中,group by的行為是相同的,都是按某幾個欄位對資料進行分組。
-
SUM:求和操作,在批SQL和流SQL中,SUM求和操作的語義上相同的,都是對每個分組的某個欄位,做求和操作。但是批SQL和流SQL的實現方式是不同的:
- 批:已經知道了所有資料,把每個分組的求和欄位拿出來相加即可。
- 流:資料是一條條進入系統的,並不知道全部資料,來一條加一條,產出一條結果。
2. group by + SUM()- 狀態
Flink SQL持續計算過程中,資料來源源不斷流入,以本文中例子來看,三條資料先後進入Flink,Flink中需要按cdate做一個全域性group by,然後對每個cdate中所有資料的price做一個聚合運算(SUM),過程如下:
- item_01: sum1=0+10
- item_02: sum2=sum1+11.1=21.1
- item_03: sum3=sum2+12.3=33.4
這就產生了個問題:每條資料的SUM計算,都要依賴上一條資料的計算結果。Flink在計算的時候,會保留這些中間結果麼?答案是:會儲存。而這些中間結果,就是一個作業的狀態(state)的一部分。
關於state的幾個關鍵問題:
- __是不是所有的作業都有狀態?__是,但是隻有聚合操作/JOIN等,state中才會儲存中間結果。簡單的運算,如filter等,state中不需要儲存中間結果。State官方文件,請參閱:Apache Flink Doc,Flink Committer解析請參考 《Apache Flink 漫談系列 – State》
- state會儲存多長時間? state會一直儲存麼?不會。流計算中state都是有過期時間的。實時計算產品中,預設是36小時。
- __state過期是什麼意思?__state過期,是指36小時前的狀態都會被刪掉。這樣做是為了節省系統儲存空間,在大視窗join計算過程中,需要儲存很多資料,如果都存下來,叢集磁碟會滿。
-
__state過期規則是什麼?__state過期規則
- 不同group by分組的state互不相關
- 與該group by分組上次state更新的時間有關,如果 __現在時間 – 某個key的state最近更新的時間>state過期時間__,則這個group by分組的state會被清理掉。
- state過期時間能調麼?能,實時計算產品中,單作業可以配置這個引數:state.backend.rocksdb.ttl.ms=129600000,單位毫秒。
- __如果state過期會對作業造成什麼影響__:
以這個例子來說,極端一點,假設我們把state過期引數設成5分鐘。如果3條原始資料進入Flink的時間__相差5分鐘以上__,以ptime定義資料進入flink的時間,如下所示:
ctime | category_id | shop_id | item_id | price | ptime |
---|---|---|---|---|---|
2018-12-04 15:44:54 | cat_01 | shop_01 | item_01 | 10 | 2018-12-04 15:45:00 |
2018-12-04 15:45:46 | cat_02 | shop_02 | item_02 | 11.1 | 2018-12-04 15:45:10 |
2018-12-04 15:46:11 | cat_01 | shop_03 | item_03 | 12.4 | 2018-12-04 15:52:00 |
此時:
- item_01(ptime=2018-12-04 15:45:00): sum1=0+10
- item_02(ptime=__2018-12-04 15:45:10__): sum2=sum1+11.1=21.1
- item_03(ptime=__2018-12-04 15:52:00__): sum3=0+12.3=12.3
item_02和item_03之間超過5min,因此state中sum2的值被清掉,導致item_03到來時,sum3的值計算錯誤。
該例子中:
- ctime就是資料產生的時間,流計算中的術語叫event time(事件時間)
- ptime是資料進入Flink的事件,流計算中術語叫process time(處理時間)
這兩個時間域是流計算的基礎概念。要正確使用流計算,還需要理解以下這兩個概念,相關文章:
《Streaming System 第一章:Streaming 101》
《Streaming System 第二章:The What- Where- When- and How of Data Processing》
SQL優化
上述SQL中,每來一條資料就要就要計算一次,在輸入數量大的情況下,容易產生效能瓶頸。每來一條資料,後端都會read和write一次state,發生序列化和反序列化操作,甚至是磁碟的 I/O 操作。對複雜場景,比如JOIN/TopN等,因此狀態的相關操作通常都會成為整個任務的效能瓶頸。
如何避免這個問題呢?使用microbatch策略。microbatch顧名思義,就是攢批。不是來一條處理一條,而是攢一批再處理。相關配置如下:
#攢批的間隔時間,使用 microbatch 策略時需要加上該配置,且建議和 blink.miniBatch.allowLatencyMs 保持一致
blink.microBatch.allowLatencyMs=5000
# 使用 microbatch 時需要保留以下兩個 minibatch 配置
blink.miniBatch.allowLatencyMs=5000
# 防止OOM,每個批次最多快取多少條資料
blink.miniBatch.size=20000。
相關知識點
-
持續查詢
-
狀態(state):
-
流計算中的時間域概念 – Process Time vs. Event Time
相關文章
- Flink入坑指南第一章-簡介
- Flutter入坑指南:編寫第一個Flutter應用Flutter
- GAMES101 作業7 踩坑指南GAM
- Docker安裝flink及避坑指南Docker
- UIStackView 入坑指南UIView
- vim 入坑指南
- Flink 實踐教程-入門(8): 簡單 ETL 作業
- rust入坑指南之ownershipRust
- Oracle函式入坑指南Oracle函式
- uni-app 入坑指南APP
- Flink從入門到放棄(入門篇2)-本地環境搭建&構建第一個Flink應用
- 第一次個人作業
- 如何跑通第一個 SQL 作業SQL
- Omi 入坑指南 Third field 事件入門事件
- 2021年 Istio 大型“入坑”指南
- Go Web開發入坑指南GoWeb
- 開發第一個Flink應用
- 第三章演算法作業演算法
- jQuery第三章課後作業jQuery
- 本地環境提交flink on yarn作業Yarn
- 針不戳!GitHub Actions 入坑指南Github
- CTF萌新入坑指南(web篇)Web
- Flutter 入坑指南(dio +fish_redux)FlutterRedux
- Omi 入坑指南 第四場 Router
- React Router v4 入坑指南React
- 個人作業登入+註冊
- 自制作業系統(一) 第一個作業系統作業系統
- mybatis入“坑”第一步MyBatis
- 踩坑指南:入門OpenTenBase之部署篇
- Rust入坑指南:海納百川Rust
- Angular 從入坑到挖坑 - 元件食用指南Angular元件
- Flutter入坑指南:開發環境搭建Flutter開發環境
- 小米安全Dayeh:《Spring Security入坑指南1》Spring
- 小米安全Dayeh:《Spring Security入坑指南2》Spring
- Omi 入坑指南 The second floor 初步接觸
- 小程式入坑指南 | 鵝廠優文
- webpack入門及踩坑應對指南Web
- python從入門到實踐第三章的課後練習作業Python