該demo基於flnk 1.10版本,由flink大佬fhueske發佈到github:https://github.com/fhueske/flink-sql-demo。
動手實踐前請先git clone https://github.com/fhueske/flink-sql-demo.git。
由於該demo內容較多,所以文章拆成了2部分,此爲第一部分。
場景和數據介紹
此demo主要演示:
- Flink SQL如何處理不同存儲系統中的數據
- Flink SQL如何使用Hive Metastore作爲外部持久化catalog
- 批流統一查詢實踐
- 幾種Join動態數據的不同方式
- 使用DDL創建表
- 在Kafka和MySQL中使用連續SQL查詢維護物化視圖
此demo的場景是電商收到訂單
該訂單系統由7張表組成
- PROD_ORDERS 主訂單表
- PROD_LINEITEM: 子訂單表
- PROD_CUSTOMER: 用戶表
- PROD_NATION: 國家表
- PROD_REGION: 地區表
- PROD_RATES: 貨幣匯率表
- PROD_RATES_HISTORY: 貨幣匯率變更歷史記錄表
根據數據的更新特徵(更新頻率、insert-only),上面的表存在不同的系統中:
- 存在KAFKA中的:PROD_ORDERS, PROD_LINEITEM, PROD_RATES_HISTORY
- 存在MySQL中的:PROD_CUSTOMER, PROD_NATION, PROD_REGION, PROD_RATES
數據下載
可以從Google Drive上下載https://drive.google.com/file/d/15LWUBGZenWaW3R_WNxqvcTQOuA_Kgtjv,然後解壓到demo的"./data"目錄。
部分同學可能下載不方便,我把數據另存了一份在csdn,受csdn文件大小限制,我分成了2個部分,請分別下載後和解壓:
part1:https://download.csdn.net/download/cndotaci/12540851
part2:https://download.csdn.net/download/cndotaci/12540856
啓動demo
# build
docker-compose build
# start
docker-compose up -d
驗證demo環境是否成功
# 驗證kafka
docker-compose exec kafka kafka-console-consumer.sh --bootstrap-server kafka:9092 --from-beginning --topic orders
docker-compose exec kafka kafka-console-consumer.sh --bootstrap-server kafka:9092 --from-beginning --topic lineitem
docker-compose exec kafka kafka-console-consumer.sh --bootstrap-server kafka:9092 --from-beginning --topic rates
# 驗證MySQL
docker-compose exec mysql mysql -Dsql-demo -usql-demo -pdemo-sql
SHOW TABLES;
DESCRIBE PROD_CUSTOMER;
SELECT * FROM PROD_CUSTOMER LIMIT 10;
quit;
以上,如果kafka能夠消費到數據,MySQL能夠查詢到數據,說明環境部署成功。
此外,demo還啓動了以下服務:
Grafana:http://localhost:3000
Minio:http://localhost:9000 (user: sql-demo, password: demo-sql)
Flink WebUI:http://localhost:8081
Flink SQL client
Hive Metastore
正題開始
啓動Flink SQL Client:
docker-compose exec sql-client ./sql-client.sh
所有的動態表(kafka表)均在默認的catalog中,所有的靜態表(MySQL表)均在Hive catalog中:
可以通過命令USE CATALOG xxx語法切換catalog,通過命令SHOW TABLES查看錶。
查詢靜態表和動態表
得益於flink-sql的批流一體功能,有很多查詢的sql語法對批處理和流處理是一致的。
對動態表做快照
- 新建一張以S3爲存儲的表
使用WATERMARK將o_ordertime定義爲延時5分鐘的eventtime:
USE CATALOG hive;
CREATE TABLE dev_orders (
o_orderkey INTEGER,
o_custkey INTEGER,
o_orderstatus STRING,
o_totalprice DOUBLE,
o_currency STRING,
o_ordertime TIMESTAMP(3),
o_orderpriority STRING,
o_clerk STRING,
o_shippriority INTEGER,
o_comment STRING,
WATERMARK FOR o_ordertime AS o_ordertime - INTERVAL '5' MINUTE
) WITH (
'connector.type' = 'filesystem',
'connector.path' = 's3://sql-demo/orders.tbl',
'format.type' = 'csv',
'format.field-delimiter' = '|'
);
- 從kafka動態表中寫一些樣例數據到上面創建的S3靜態表dev_orders
Flink SQL> INSERT INTO dev_orders SELECT * FROM default_catalog.default_database.prod_orders;
[INFO] Submitting SQL update statement to the cluster...
[INFO] Table update statement has been successfully submitted to the cluster:
Job ID: 31e3b34814863ceef82201208f1b68c1
-
提交INSERT語句後,可以通過Flink Web UI(http://localhost:8081)查看正在運行的任務
-
手動取消任務
-
打開Minio(http://localhost:9000),可以看到剛剛INSERT操作寫入的文件
-
在SQL client中查看剛剛寫入的數據
SELECT * FROM dev_orders;
SELECT COUNT(*) AS rowCnt FROM dev_orders;
查詢快照數據
- 使用批處理引擎效率更高
SET execution.type=batch;
- 計算不同幣種每分鐘的交易額
SELECT
CEIL(o_ordertime TO MINUTE) AS `minute`,
o_currency AS `currency`,
SUM(o_totalprice) AS `revenue`,
COUNT(*) AS `orderCnt`
FROM dev_orders
GROUP BY
o_currency,
CEIL(o_ordertime TO MINUTE);
- 使用流處理引擎執行上面的查詢
SET execution.type=streaming;
可見同樣的sql,使用批處理引擎和流處理引擎會得到同樣的結果。
- 下面將查詢結果的返回格式由table改爲changelog
SET execution.result-mode=changelog;
再次執行上面的sql:
可見使用流處理引擎時,結果是通過插入(+)、刪除(-)、再插入(+)的方式輸出的,也就是流引擎會實時計算每一條數據並輸出計算結果,相對而言批處理引擎則將全量數據統一計算,一次性返回計算結果。
- 通過滾動窗口簡化上面需求的查詢
SELECT
TUMBLE_END(o_ordertime, INTERVAL '1' MINUTE) AS `minute`,
o_currency AS `currency`,
SUM(o_totalprice) AS `revenue`,
COUNT(*) AS `orderCnt`
FROM dev_orders
GROUP BY
o_currency,
TUMBLE(o_ordertime, INTERVAL '1' MINUTE);
可見返回結果中,只有數據插入(+),並沒有刪除(-),這是因爲窗口計算時,flink會等到窗口結束後統一計算窗口內的數據並返回結果,而不會每收到一條數據就計算一次然後通過刪除舊數據插入新數據的方式更新。通過這種方式,簡化了數據計算的複雜度,提高了性能。
- 同樣,上面的窗口計算也可以使用批處理引擎進行
SET execution.result-mode=table;
SET execution.type=batch;
計算結果與使用流處理引擎一致:
查詢流數據
- 在kafka表上執行與上面示例相同的查詢
SET execution.type=streaming;
- 僅修改sql中的表名爲kafka表
SELECT
TUMBLE_END(o_ordertime, INTERVAL '1' MINUTE) AS `minute`,
o_currency AS `currency`,
SUM(o_totalprice) AS `revenue`,
COUNT(*) AS `orderCnt`
FROM default_catalog.default_database.prod_orders
GROUP BY
o_currency,
TUMBLE(o_ordertime, INTERVAL '1' MINUTE);
得到相同的查詢結果:
綜上,對於靜態數據(S3靜態表),無論使用批處理引擎還是流處理引擎,同樣的sql查詢得到的計算結果是一致的。
使用流處理引擎,同樣的sql對於靜態數據和動態數據(kafka流表)也會得到相同的計算結果,這就是flink流批一體的設計原則——將批數據當做一種有限的流,這樣就可以在流和批上共享大部分代碼。同時,可以單獨對批處理進行特有的優化。