Flink(1.11)高階程式設計——FlinkSQL

平平無奇小碼農發表於2020-12-16

八、Flink SQL 程式設計

8.1 Table API

①建立表的執行環境

StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

②將流轉成動態表 ——> fromDataStream

Table sensorTable = tableEnv.fromDataStream(sensorDS, $("ts"), $("id"), $("vc").as("ergou"));

③使用TableAPI對動態表進行操作,返回一個結果表

Table resultTable = sensorTable
                    .where($("id").isEqual("sensor_1"))
                	.select($("id"), $("ergou"));

④ 1 將動態錶轉換成流,輸出 ——> toAppendStream \ toRetractStream

正常:toAppendStream

DataStream<Tuple2<String, Integer>> resultDS = 
    tableEnv
    	.toAppendStream(resultTable, TypeInformation.of(new TypeHint<Tuple2<String, Integer>>() {}));

撤回流:toRetractStream

涉及到資料的更新,要使用 撤回流   
// 撤回流實現方式:
//  前面加一個boolean型別的標誌位: true表示插入,false表示撤回
// 更新的邏輯: 將 舊的結果 置為 false,將 新的結果 插入,置為 true
// DataStream<Tuple2<Boolean, Row>> resultDS = tableEnv.toRetractStream(resultTable, Row.class);
DataStream<Tuple2<Boolean, Row>> resultDS =
    tableEnv
    	.toRetractStream(resultTable, Row.class);

④ 2 將動態流寫入檔案系統 ——> executeInsert

1)、把檔案系統抽象成Table:Connect

1.呼叫connect,連線外部系統
2.呼叫withFormat,指定資料格式
3.呼叫withSchema,指定抽象成Table的表結構(欄位名,欄位型別)
4.呼叫creatTemporaryTable,指定Table的表名
tableEnv
	.connect(new FileSystem().path("output/flink.txt"))
	.withFormat(new OleCsv().filedDelimiter("|"))
	.withSchema(
                        new Schema()
                                .field("a", DataTypes.STRING())
                                .field("tt", DataTypes.BIGINT())
                                .field("cc", DataTypes.INT())
                )
               .createTemporaryTable("fsTable");

2)、通過往表插入資料的方法,把結果Sink到檔案系統 ——> executeInsert

TableResult fsTable = resultTable.executeInsert("fsTable");

8.2 SQL API

① 建立表的執行環境

StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

② 將流轉換成動態表 ——> createTemporaryView

tableEnv.createTemporaryView("sensor", sensorDS, $("id"), $("ts"), $("vc"));
tableEnv.createTemporaryView("sensor1", sensorDS1, $("id"), $("ts"), $("vc"));

可以通過名字獲取Table物件

Table sensorTable = tableEnv.from("sensor");

③ 使用SQL對動態表進行操作,返回一個結果表 ——> sqlQuery

在 sqlQuery( ) 方法裡寫sql語句

Table resultTable = tableEnv
   .sqlQuery("select * from sensor where id='sensor_1'"); // 條件查詢

//.sqlQuery("select id,count(id) cnt from sensor group by id"); // 分組查詢
//.sqlQuery("select * from sensor right join sensor1 on sensor.id=sensor1.id"); // 分組查詢
//.sqlQuery("select * from sensor where id not in (select id from sensor1)"); // 範圍查詢
//.sqlQuery("select (case id when 'sensor_1' then 1 else 0 end) test from sensor"); // case when查詢

返回一個結果表

tableEnv.createTemporaryView("result", resultTable);

④ 1 將動態錶轉換成流,輸出 ——> toRetractStream

DataStream<Tuple2<Boolean,Row>> resultDS = tableEnv.toRetractStream(resultTable,Row.class)

④ 2 將其寫入外部系統

1)、使用SQL將外部系統抽象成Table

tableEnv.executeSql("create table fsTable (a String,b bigint,c int) " +
                "with (" +
                "'connector'='filesystem'," +
                "'path'='output'," +
                "'format'='csv'" +
                ")");

2)、對其進行寫入,相當於sink到外部系統 ——> executeSql

關鍵字Bug:result =》 1.要麼避免和關鍵字重複 2.要麼加 ’ '

tableEnv.executeSql("insert into fsTable select * from 'result'");

8.3 FlinkSQL 整合Hive

〇 引數準備

String catalogName = "myhive"; //catalog名稱
String defaultDatabase = "flinktest"; // hive資料庫,設定為這個catalog下的預設庫
String hiveConfDir = "F:\\atguigu\\01_course\\code\\hive-conf"; // hive配置檔案所在的目錄
//String hiveConfDir = "/opt/module/hive/conf"; // hive配置檔案所在的目錄
String version = "1.2.1";

①建立hive的catalog

HiveCatalog hiveCatalog = new HiveCatalog(catalogName, defaultDatabase, hiveConfDir, version);

②註冊catalog

tableEnv.registerCatalog(catalogName,hiveCatalog);

③指定catalog(不指定,就是一個預設的catalog,叫default_catalog)

tableEnv.useCatalog(catalogName);

④ 指定sql語法為Hive sql

tableEnv.getConfig().setSqlDialect(SqlDialect.HIVE);

⑤使用sql操作hive表

//將流轉成一個動態表
tableEnv.createTemporaryView("inputTable", inputDS);
//執行sql語句
tableEnv.executeSql("insert into test select * from inputTable");

8.4 視窗

8.4.1 分組視窗(Group Windows)

〇 準備

 //建立表的執行環境
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
//將流轉換成動態表,並給表去個別名
tableEnv.createTemporaryView("sensor",sensorDS,$("id"), $("ts").rowtime(), $("vc"));
//獲取該表的物件
Table sensorTable = tableEnv.from("sensor");

① 滾動視窗

概念

1.	over:定義視窗長度
2.	on:用來分組(按時間間隔)或者排序(按行數)的時間欄位
3.	as:別名,必須出現在後面的groupBy中

Table API 寫法

sensorTable
         .window(Tumble.over(lit(3).seconds()).on($("ts").as("w"))

② 滑動視窗

概念

1.	over:定義視窗長度
2.	every:定義滑動步長
3.	on:用來分組(按時間間隔)或者排序(按行數)的時間欄位
4.	as:別名,必須出現在後面的groupBy中

Table API 寫法

sensorTable													  .window(Slide.over(lit(3).seconds()).every(lit(2).seconds()).on($("ts")).as("w"));

③ 會話視窗

概念

1.	withGap:會話時間間隔
2.	on:用來分組(按時間間隔)或者排序(按行數)的時間欄位
3.	as:別名,必須出現在後面的groupBy中

Table API 寫法

sensorTable    
	.groupBy($("w"),$("id"))
	.select($("id").count().as("cnt"),$("w").start(),$("w").end());

SQL 寫法 【時間引數不加s】

// TODO 3.使用 SQL 對 Table 開 GroupWindow
        Table resultTable = tableEnv.sqlQuery(
		"select id,count(id) cnt," +
         "TUMBLE_START(ts,INTERVAL '3' SECOND) as window_start," +
         "TUMBLE_END(ts,INTERVAL '3' SECOND) as window_end " +
           "from sensor " +
          "group by TUMBLE(ts,INTERVAL '3' SECOND),id"
        );

⑤ 將動態錶轉換成撤回流

tableEnv.toRetractStream(resultTable,Row.class).print();

8.4.2 Over Windows

概念

Group Windows在SQL查詢的Group BY子句中定義。與使用常規GROUP BY子句的查詢一樣,使用GROUP BY子句的查詢會計算每個組的單個結果行。
SQL支援以下Group視窗函式:
	TUMBLE(time_attr, interval)
定義一個滾動視窗,第一個引數是時間欄位,第二個引數是視窗長度。
	HOP(time_attr, interval, interval)【必須寫在groupby裡面】
定義一個滑動視窗,第一個引數是時間欄位,第二個引數是視窗滑動步長,第三個是視窗長度。
	SESSION(time_attr, interval)
定義一個會話視窗,第一個引數是時間欄位,第二個引數是視窗間隔(Gap)。

另外還有一些輔助函式,可以用來選擇Group Window的開始和結束時間戳,以及時間屬性。
這裡只寫TUMBLE_*,滑動和會話視窗是類似的(HOP_*,SESSION_*)。
	TUMBLE_START(time_attr, interval)
	TUMBLE_END(time_attr, interval)
	TUMBLE_ROWTIME(time_attr, interval)
	TUMBLE_PROCTIME(time_attr, interval)

程式碼

//獲取表執行環境
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
//將流轉表,並給表起一個名字
tableEnv.createTemporaryView("sensor", sensorDS, $("id"), $("ts").rowtime(), $("vc"));
//獲取表物件
Table sensorTable = tableEnv.from("sensor");

Table API 寫法

 sensorTable.window(
                Over
                .partitionBy($("id"))
                .orderBy($("ts").desc())
                .preceding(UNBOUNDED_ROW)
                .following(CURRENT_ROW)
                .as("ow")
        )
                .select($("*"),$("id").count().over($("ow")));

SQL 寫法

  Table resultTable = tableEnv.sqlQuery(
                "select *," +
                        "count(id) over(partition by id order by ts desc) as cow\n" +
                        "from sensor"
        );
//錶轉流
tableEnv.toRetractStream(resultTable, Row.class).print();

env.execute();

8.5 案例實操

熱門商品統計 TopN

public static void main(String[] args) throws Exception {
        // 1.建立執行環境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);

        // 2.讀取資料
        SingleOutputStreamOperator<UserBehavior> userBehaviorDS = env
                .readTextFile("input/UserBehavior.csv")
                .map(new MapFunction<String, UserBehavior>() {
                    @Override
                    public UserBehavior map(String value) throws Exception {
                        String[] datas = value.split(",");
                        return new UserBehavior(
                                Long.valueOf(datas[0]),
                                Long.valueOf(datas[1]),
                                Integer.valueOf(datas[2]),
                                datas[3],
                                Long.valueOf(datas[4])
                        );
                    }
                })
                .assignTimestampsAndWatermarks(
                        WatermarkStrategy
                                .<UserBehavior>forBoundedOutOfOrderness(Duration.ofMinutes(1))
                                .withTimestampAssigner((data, ts) -> data.getTimestamp() * 1000L)
                );

        // TODO 3.使用 FlinkSQL 實現
        //flinkSQL只支援blinkplanner
        EnvironmentSettings settings = EnvironmentSettings.newInstance()
                .useBlinkPlanner()
                .inStreamingMode()
                .build();
        //blink基於流的模式
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env, settings);
        // 統計 每5分鐘輸出最近一小時的熱門商品點選,只需要 商品ID、行為、時間戳
        Table userBehaviorTable = tableEnv.fromDataStream(userBehaviorDS,$("itemId"), $("behavior"), $("timestamp").rowtime().as("ts"));
        // 3.1 資料的準備:過濾出pv行為、分組、開窗、求和統計、攜帶視窗資訊
        Table aggTable = userBehaviorTable.where($("behavior").isEqual("pv"))
                .window(
                        Slide
                                .over(lit(1).hours()).every(lit(5).minutes())
                                .on($("ts"))
                                .as("w")
                )
                .groupBy($("w"), $("itemId"))
                .select($("itemId"),
                        $("itemId").count().as("itemCount"),
                        $("w").end().as("windowEnd"));
        //把Table轉成DataStream,再轉成Table
        DataStream<Row> aggDS = tableEnv.toAppendStream(aggTable, Row.class);
        tableEnv.createTemporaryView("aggTable",aggDS,$("itemId"),$("itemCount"),$("windowEnd"));

        //3.2   實現TopN
        Table tableResult = tableEnv.sqlQuery(
                "select * " +
                        "from (" +
                        "select *,row_number() over(partition by windowEnd order by itemCount desc) as rn from aggTable) " +
                        "where rn <= 3");
        tableEnv.toRetractStream(tableResult, Row.class).print();


        env.execute();


    }

相關文章