大数据开发Flink DataSet 与Table Api(第四十七篇)

363 阅读4分钟

一、DataSet API

1.1、DataSet API 之 DataSource
  1. 基于集合 fromCollection(Collection)
  2. 基于文件 readTextFile(Path)
1.2、DataSet API 之 Transformation
算子解释
map输入一个元素进行处理,返回一个元素
mapPartition类似map,一次处理一个分区的数据
flatMap输入一个元素进行处理,可以返回多个元素
filter对数据进行过滤,符合条件的数据会被留下
reduce对当前元素和上一次的结果进行聚合操作
aggregatesum(),min(),max()等
package com.strivelearn.flink.dataset;
​
import java.util.Arrays;
import java.util.Iterator;
​
import org.apache.flink.api.common.functions.MapPartitionFunction;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.operators.MapPartitionOperator;
import org.apache.flink.util.Collector;
​
/**
 * @author strivelearn
 * @version BatchMapPartitionJava.java, 2023年01月15日
 */
public class BatchMapPartitionJava {
    public static void main(String[] args) throws Exception {
        // 获取执行环境
        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        // 生成数据源
        DataSource<String> stringDataSource = env.fromCollection(Arrays.asList("hello you", "hello me"));
        // 每次处理一个分区的数据
        MapPartitionOperator<String, String> stringStringMapPartitionOperator = stringDataSource.mapPartition(new MapPartitionFunction<String, String>() {
            @Override
            public void mapPartition(Iterable<String> iterable, Collector<String> collector) throws Exception {
                Iterator<String> iterator = iterable.iterator();
                while (iterator.hasNext()) {
                    String line = iterator.next();
                    String[] words = line.split(" ");
                    for (String word : words) {
                        collector.collect(word);
                    }
                }
            }
        });
        stringStringMapPartitionOperator.print();
    }
}
算子解释
distinct返回数据集中去重之后的元素
join内连接
outerJoin外连接
cross获取两个数据集的笛卡尔积
union返回多个数据集的总和,数据类型需要一致
first-n获取集合中的前N个元素
内连接代码示范
package com.strivelearn.flink.dataset;
​
import java.util.ArrayList;
import java.util.Arrays;
​
import org.apache.flink.api.common.functions.JoinFunction;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
​
/**
 * @author strivelearn
 * @version BatchJoinJava.java, 2023年01月15日
 */
public class BatchJoinJava {
    public static void main(String[] args) throws Exception {
        // 获取执行环境
        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        // 初始化第一份数据 Tuple2<用户id,用户姓名>
        ArrayList<Tuple2<Integer, String>> data1 = new ArrayList<>();
        data1.add(new Tuple2<>(1, "lily"));
        data1.add(new Tuple2<>(2, "tom"));
        data1.add(new Tuple2<>(3, "lucy"));
        DataSource<Tuple2<Integer, String>> datasource1 = env.fromCollection(data1);
​
        // 初始化第二份数据 Tuple2<用户id,所在城市>
        ArrayList<Tuple2<Integer, String>> data2 = new ArrayList<>();
        data2.add(new Tuple2<>(1, "suzhou"));
        data2.add(new Tuple2<>(2, "hangzhou"));
        data2.add(new Tuple2<>(4, "shanghai"));
        DataSource<Tuple2<Integer, String>> datasource2 = env.fromCollection(data2);
​
        datasource1.join(datasource2).where(0).equalTo(0).with(new JoinFunction<Tuple2<Integer, String>, Tuple2<Integer, String>, Tuple3<Integer, String, String>>() {
            @Override
            public Tuple3<Integer, String, String> join(Tuple2<Integer, String> first, Tuple2<Integer, String> second) throws Exception {
                return new Tuple3<>(first.f0, first.f1, second.f1);
            }
        }).print();
    }
}

输出结果 (1,lily,suzhou) (2,tom,hangzhou)

外连接代码演示
package com.strivelearn.flink.dataset;
​
import org.apache.flink.api.common.functions.JoinFunction;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
​
import java.util.ArrayList;
​
/**
 * 外连接
 * @author strivelearn
 * @version BatchOuterJoinJava.java, 2023年01月15日
 */
public class BatchOuterJoinJava {
    public static void main(String[] args) throws Exception {
        // 获取执行环境
        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        // 初始化第一份数据 Tuple2<用户id,用户姓名>
        ArrayList<Tuple2<Integer, String>> data1 = new ArrayList<>();
        data1.add(new Tuple2<>(1, "lily"));
        data1.add(new Tuple2<>(2, "tom"));
        data1.add(new Tuple2<>(3, "lucy"));
        DataSource<Tuple2<Integer, String>> datasource1 = env.fromCollection(data1);
​
        // 初始化第二份数据 Tuple2<用户id,所在城市>
        ArrayList<Tuple2<Integer, String>> data2 = new ArrayList<>();
        data2.add(new Tuple2<>(1, "suzhou"));
        data2.add(new Tuple2<>(2, "hangzhou"));
        data2.add(new Tuple2<>(3, "shanghai"));
        DataSource<Tuple2<Integer, String>> datasource2 = env.fromCollection(data2);
​
        datasource1.join(datasource2).where(0).equalTo(0).with(new JoinFunction<Tuple2<Integer, String>, Tuple2<Integer, String>, Tuple3<Integer, String, String>>() {
            @Override
            public Tuple3<Integer, String, String> join(Tuple2<Integer, String> first, Tuple2<Integer, String> second) throws Exception {
                return new Tuple3<>(first.f0, first.f1, second.f1);
            }
        }).print();
    }
}

输出:

(3,lucy,null) (1,lily,suzhou) (2,tom,hangzhou)

笛卡尔积 cross
package com.strivelearn.flink.dataset;
​
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
​
import java.util.Arrays;
​
/**
 * cross 获取两个数据集的笛卡尔积
 * @author strivelearn
 * @version BatchCrossJava.java, 2023年01月15日
 */
public class BatchCrossJava {
    public static void main(String[] args) throws Exception {
        // 获取执行环境
        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        // 初始化第一份数据
        DataSource<Integer> dataSource1 = env.fromCollection(Arrays.asList(1, 2));
        // 初始化第二份数据
        DataSource<String> dataSource2 = env.fromCollection(Arrays.asList("a", "b"));
        dataSource1.cross(dataSource2).print();
    }
}
​

输出

(1,a) (1,b) (2,a) (2,b)

获取集合中的前N个元素
package com.strivelearn.flink.dataset;
​
import org.apache.flink.api.common.operators.Order;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.tuple.Tuple2;
​
import java.util.ArrayList;
import java.util.Arrays;
​
/**
 * first-n 获取集合的前N个元素
 * @author xys
 * @version BatchFirstNJava.java, 2023年01月15日
 */
public class BatchFirstNJava {
    public static void main(String[] args) throws Exception {
        // 获取执行环境
        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        ArrayList<Tuple2<Integer, String>> dataSource = new ArrayList<>();
        dataSource.add(new Tuple2<>(1, "lily"));
        dataSource.add(new Tuple2<>(2, "tom"));
        dataSource.add(new Tuple2<>(3, "lucy"));
        dataSource.add(new Tuple2<>(4, "zhangsan"));
        dataSource.add(new Tuple2<>(5, "ali"));
        dataSource.add(new Tuple2<>(6, "hero"));
        DataSource<Tuple2<Integer, String>> tuple2DataSource = env.fromCollection(dataSource);
        // 获取前3条数据,按照数据插入的顺序
        tuple2DataSource.first(3).print();
​
        System.out.println("======分割线======");
​
        // 根据数据中的第一列进行分组,获取每组的前2个元素
        tuple2DataSource.groupBy(0).first(2).print();
​
        System.out.println("======分割线======");
​
        // 根据数据中的第一列分组,再根据第二列进行组内排序【倒序】,获取每组的前2个元素
        // 分组排序取TopN
        tuple2DataSource.groupBy(0).sortGroup(1, Order.DESCENDING).first(2).print();
    }
}

输出

(1,lily) (2,tom) (3,lucy) ======分割线====== (3,lucy) (1,lily) (5,ali) (6,hero) (4,zhangsan) (2,tom) ======分割线====== (3,lucy) (1,lily) (5,ali) (6,hero) (4,zhangsan) (2,tom)

1.3、DataSet API 之 DataSink
  1. writeAsText() 将元素以字符串形式逐行写入,这些字符串通过调用每个元素的toString()方法来获取
  2. writeAsCsv() 将元祖以逗号分隔写入文件中,行和字段之间的分隔符是可配置的,每个字段的值来自对象的toString方法
  3. print() 打印每个元素的toString()方法的值到标准输出

二、Flink Table API 和SQL

是一种关系型的API,用户可以像操作MySQL数据库表一样操作数据,而不需要写代码。更不需要手工对代码进行调优

实现流批统一的 Blink planner 中由于没有了 DataSet 的概念,已经不再使用 BatchTableEnvironment

<!--想要在本地ide运行添加此依赖-->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-table-planner_2.12</artifactId>
    <version>1.16.0</version>
    <scope>test</scope>
</dependency><!--使用table api 和sql添加此依赖-->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-table-api-java-bridge</artifactId>
    <version>1.16.0</version>
    <scope>provided</scope>
</dependency>
package com.strivelearn.flink.tableapi;
​
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.TableEnvironment;
​
import static org.apache.flink.table.api.Expressions.$;
​
/**
 * @author strivelearn
 * @version TableApiAndSqlOpJava.java, 2023年01月15日
 */
public class TableApiAndSqlOpJava {
    public static void main(String[] args) {
        EnvironmentSettings streamMode = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
        TableEnvironment sTableEnv = TableEnvironment.create(streamMode);
​
        sTableEnv.executeSql("create table myTable" + "(id int,name String)" + "with('connector.type'='filesystem'," + "'connector.path'='/Users/strivelearn/Desktop',"
                             + "'format.type'='csv')");
        // 使用table api 进行查询
        Table result = sTableEnv.from("myTable").select($("id"), $("name")).filter($("id").isGreater(1));
        result.execute().print();
​
        // 使用SQL实现数据查询和过滤操作
        Table table = sTableEnv.sqlQuery("select id,name from myTable where id>1");
        table.execute().print();
    }
}
2.1、DataStream、DataSet和Table之间的互相转换
  1. 使用DataStream创建表,主要包含下面两种情况:

    • 使用DataStream创建view视图
    • 使用DataStream创建Table对象
  2. 使用DataSet创建表

    注意:此时只能使用旧的执行引擎,新的blink执行引擎不支持和DataSet转换

  3. 将表转换为DataSteam

    流式查询的结果Table会被动态的更新,即每个新的记录到达输入流时结果就会发生变化,因此,转换此动态查询的DataStream需要对表的更新进行编码。

    Append Mode:这个模式只适用于当动态表仅由insert操作进行修改时,之前添加的数据不会被更新

    Retract Mode:可以使用此模型,它使用一个Boolean标识来编码insert和delete操作

  4. 将表转换为DataSet