既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
* types.
*
* @deprecated use {@link AvroParquetWriters} instead. // 看这部分是建议使用AvroParquetWriters
*/
@Deprecated // 这里已经标注了过时
public class ParquetAvroWriters {
/\*\*
* Creates a ParquetWriterFactory for an Avro specific type. The Parquet writers will use the
* schema of that specific type to build and write the columnar data.
*
* @param type The class of the type to write.
*/
public static ParquetWriterFactory forSpecificRecord(
Class type) {
return AvroParquetWriters.forSpecificRecord(type);
}
* AvroParquetWriters.forReflectRecord(方式三)
这里就先介绍一下`AvroParquetWriters.forReflectRecord`的使用方式,我们在使用`FileSink`时最好配合`Checkpoint`使用,不然文件只会出现`inprogress`状态,感兴趣的可以自己实验一下,我在[Flink中FileSink的使用]( )演示了加`Checkpoint`和不加`Checkpoint`的区别感兴趣的可以看一下.
代码模板内容比较简单,直接代码演示:
import com.jin.bean.User;
import com.jin.schema.UserSchemaBean;
import org.apache.flink.connector.file.sink.FileSink;
import org.apache.flink.core.fs.Path;
import org.apache.flink.formats.parquet.ParquetWriterFactory;
import org.apache.flink.formats.parquet.avro.AvroParquetWriters;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.sink.filesystem.bucketassigners.DateTimeBucketAssigner;
import org.apache.flink.streaming.api.functions.sink.filesystem.rollingpolicies.OnCheckpointRollingPolicy;
/**
* @Author: J
* @Version: 1.0
* @CreateTime: 2023/6/28
* @Description: 测试
**/
public class FlinkFileSinkForParquet {
public static void main(String[] args) throws Exception {
// 创建流环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 设置并行度
env.setParallelism(1);
// 每30秒作为checkpoint的一个周期
env.enableCheckpointing(30000);
// 两次checkpoint间隔最少是20秒
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(20000);
// 程序取消或者停止时不删除checkpoint
env.getCheckpointConfig().setExternalizedCheckpointCleanup(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN\_ON\_CANCELLATION);
// checkpoint必须在60秒结束,否则将丢弃
env.getCheckpointConfig().setCheckpointTimeout(60000);
// 同一时间只能有一个checkpoint
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
// 设置EXACTLY\_ONCE语义,默认就是这个
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY\_ONCE);
// checkpoint存储位置
env.getCheckpointConfig().setCheckpointStorage("file:///Users/xxx/data/testData/checkpoint");
// 添加数据源(这里使用的是自定义数据源CustomizeSource,方便测试)
DataStreamSource<CustomizeBean> sourceStream = env.addSource(new CustomizeSource());
// 将数据流中的数据存储到bean对象中
SingleOutputStreamOperator<User> userMapStream = sourceStream.map(bean -> new User(bean.getName(), bean.getAge(), bean.getGender(), bean.getHobbit()));
// 构建parquetWriterFactory
ParquetWriterFactory<User> parquetWriterFactory2 = AvroParquetWriters.forReflectRecord(User.class);
// 构建FileSink
FileSink<User> parquetFileSink = FileSink
// 使用Bulk模式,并配置路径和对应的schema
.forBulkFormat(new Path("/Users/xxx/data/testData/"), parquetWriterFactory2)
// 分桶策略,使用默认的
.withBucketAssigner(new DateTimeBucketAssigner<User>())
// 每100毫秒检查一次分桶
.withBucketCheckInterval(100)
// 滚动策略,Bulk的滚动策略只有一种,就是发生Checkpoint的时候才进行滚动(为了保证列式文件的完整性)
.withRollingPolicy(OnCheckpointRollingPolicy.build())
.build();
// 输出到文件
userMapStream.sinkTo(parquetFileSink);
env.execute();
}
}
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
class User {
private String name;
private int age;
private String gender;
private String hobbit;
}
代码中注释很详细了,具体使用看注释即可。这里说明一下为什么`forBulkFormat`的滚动策略只有`OnCheckpointRollingPolicy`而不是像`forRowFormat`那样可以通过时间和文件大小来控制文件滚动,注释中我也讲了是为了保证列式存储文件的完整性,因为列式文件中记录了很多信息,并不想行式存储文件一行一行的写就行,写到某一行直接停了也不影响文件的使用,而列式存储文件中不单单是记录了数据本身还有对应的字段类型、文件头信息、文件尾信息、切片索引等很多信息,如果在写入数据时某一刻直接停止了,而文件还没有生成完整的信息那就会导致这个列士存储文件根本不具备使用性,是无法进行解析的。
就比如说`ParquetFile`,它的文件结构如下图

可以看到文件的结构信息是很复杂的,如果感兴了解一下可以看[数据存储格式]( )这篇文章了解一下,这里就不细说了,内容还是比较多的.
* AvroParquetWriters.forSpecificRecord(方式二)
`forSpecificRecord`的使用不像`forReflectRecord`那样自定义一个`bean`接收数据就行了,使用`forSpecificRecord`还要结合一下`Apache avro`的[官网](avro.apache.org)看一下,下面我就介绍一下如何使用`forSpecificRecord`.
`avro`的使用有两种方式一是通过`API`直接调用的方式,二通过配置`avsc`文件然后进行编译的方式,在代码中我们使用的第二种方式,使用第一种方式同样会出现很多`schema`的信息在代码中写死修改起来会比较复杂的问题,而且对`avro`的`API`也要足够熟悉,学习成本还是有的.
1. 在`resource`目录中创建`avsc`文件,文件内容如下
```
{
"namespace": "com.jin.schema",
"type": "record",
"name": "UserSchemaBean",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": "int"},
{"name": "gender", "type": "string"},
{"name": "hobbit", "type": "string"}
]
}
```
文件中的内容就是`schema`信息,这里我相信大家都能看得明白.`"namespace": "com.jin.schema"`编译后自动创建的`bean`的存储位置,`"name": "UserSchemaBean"`就是配置生成`bean`的名称,`fields`中就是配置生成`bean`的成员变量和对应的数据类型.
官网演示的`avsc`文件内容如下:
```
{"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite\_number", "type": ["int", "null"]},
{"name": "favorite\_color", "type": ["string", "null"]}
]
}
```
编译后就会根据`avsc`文件中的`schema`信息在配置好的目录中自动创建`bean`.
2. 在`Maven`中添加`avsc`文件编译插件
官网内容如下:
```
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.11.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
```
要注意`<sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>`是已经配置完的`avsc`文件的位置,像是我就是在原有的`resource`目录下配置的就要将内容改成`<sourceDirectory>${project.basedir}/src/main/resource/</sourceDirectory>`否则在编译时就会报错找不到对应的目录或文件,如果想直接使用`<sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>`那就在项目的`main`目录下创建一个`avro`目录并将目录性质改为`Source root`(这个如果不会可自行百度,关键字我都已经提供了).
我的项目中实际配置如下:
```
<!-- avro插件 -->
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.10.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/resources/</sourceDirectory>
<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
```
选择插件的版本时要注意依赖冲突问题,我们要先看一下Flink的`flink-avro`下的`org.apache.avro:avro`是什么版本,如下图:

可以看到`1.15.3`的`org.apache.avro:avro`的版本是`1.10.0`,所以我选择的插件也是这个版本.
3. 编译


**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**