java 实现 flink 读 kafka 写 paimon

一.背景

         在数字化转型加速的当下,企业业务场景日益复杂,数据呈现出 高并发、低延迟、多源异构 的核心特征。日志采集、交易流水、用户行为等实时数据源源不断产生,这类数据不仅需要被快速接收、处理,更需要长期存储以便后续离线分析、数据回溯与合规审计,形成 “实时处理 + 离线复用” 的完整数据闭环。

         传统数据处理架构中,实时数据常通过消息队列(如 Kafka)暂存,但 Kafka 仅适用于流式数据的临时存储与传输,不支持高效的离线查询、数据更新与分层管理;而传统数据仓库(如 Hive)虽能满足离线分析需求,却难以应对实时数据的低延迟处理诉求,导致 “实时处理” 与 “长期存储” 脱节,无法兼顾业务对数据时效性和复用性的双重要求。

         在此背景下,各类组件的协同架构成为解决该痛点的关键:

  1. Kafka 作为实时数据接入层:凭借高吞吐、低延迟、高容错的特性,Kafka 已成为企业实时数据流转的核心枢纽,承担着日志、交易、传感器等实时数据流的接收与分发任务,是实时数据处理的 “数据源头”;
  2. Flink 作为实时计算引擎:Flink 具备强大的流批一体处理能力,支持 Exactly-Once 语义、复杂事件处理(CEP)、状态管理等核心特性,能够高效消费 Kafka 中的实时数据流,完成数据清洗、转换、聚合、关联等计算逻辑,是连接 “数据源头” 与 “存储终端” 的核心计算桥梁;
  3. Paimon 作为湖仓存储层:Paimon(原 Flink Table Store)作为开源湖仓一体存储方案,兼具数据湖的灵活性与数据仓库的高效查询能力,支持实时写入、批量读取、数据更新、分区管理、索引优化等特性,能完美承接 Flink 处理后的实时数据,既满足低延迟的实时查询需求,又支持离线分析、数据回溯等场景,解决了传统存储方案 “实时写入与离线复用不可兼得” 的问题。

         基于此,采用 Java 语言实现 Flink 读 Kafka 写 Paimon 的架构,本质是构建一套 “实时接入 - 实时计算 - 湖仓存储” 的端到端解决方案。Java 作为 Flink、Kafka、Paimon 生态的核心开发语言,具备完善的 API 支持、丰富的生态工具与成熟的企业级实践,能够确保架构的稳定性、可扩展性与可维护性。该方案可广泛应用于实时数据仓库构建、用户行为分析、实时监控告警、交易数据实时归档等业务场景,帮助企业打通数据流转全链路,实现数据价值的快速释放与长期复用。

二.具体实现

1.创建java工程,引入依赖

        <dependency>
            <groupId>org.apache.paimon</groupId>
            <artifactId>paimon-flink-1.18</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-api-java-bridge</artifactId>
            <version>1.18.1</version>
        </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-java</artifactId>
        <version>1.18.1</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-streaming-java</artifactId>
        <version>1.18.1</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-clients</artifactId>
        <version>1.18.1</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-connector-kafka</artifactId>
        <version>3.0.2-1.18</version>
      </dependency>

2.定义kafka数据源反序列化类

public class DataSchemaDeserialization implements KafkaDeserializationSchema<ConsumerRecord<String,String>> {

    @Override
    public boolean isEndOfStream(ConsumerRecord<String,String> nextElement) {
        return false;
    }

    @Override
    public ConsumerRecord<String,String> deserialize(ConsumerRecord<byte[], byte[]> record) throws Exception {

        return new ConsumerRecord<String, String>(
                record.topic(),
                record.partition(),
                record.offset(),
                record.timestamp(),
                record.timestampType(),
                record.checksum(),
                record.serializedKeySize(),
                record.serializedValueSize(),
                record.key() != null ? new String(record.key()) : null,
                record.value() != null ? new String(record.value(), StandardCharsets.UTF_8) : null);
    }


    @Override
    public TypeInformation<ConsumerRecord<String,String>> getProducedType() {
        return TypeInformation.of(new TypeHint<ConsumerRecord<String,String>>() {
        });
    }
}

3.定义kafka数据源(json格式)

Properties props = new Properties();
        props.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG,kafka地址);
        props.put(CommonClientConfigs.GROUP_ID_CONFIG,消费组);
        props.put(ENABLE_IDEMPOTENCE_CONFIG, "true");

        FlinkKafkaConsumerBase flinkKafkaConsumer = new FlinkKafkaConsumer<>(kafka topic, new DataSchemaDeserialization(), props).setStartFromGroupOffsets();

4.转换数据流为Row格式

DataStream<Row> input = env.addSource(flinkKafkaConsumer).map(new MapFunction<ConsumerRecord, Row>() {
            @Override
            public Row map(ConsumerRecord value) throws Exception {

                JSONObject jsonObject = JSONObject.parseObject(value.value().toString());

                Row row = Row. withPositions(3);
                    row. setField(0, jsonObject.getString("xxxx"));
                    row. setField(1, jsonObject.getString("xxx"));
                    row. setField(2,  LocalDateTime.now());

                return row;
            }
        });

5.定义catalog

Options catalogOptions = new Options();
        catalogOptions.set("warehouse", "hdfs://xxx.xxx");
        Catalog catalog = FlinkCatalogFactory.createPaimonCatalog(catalogOptions);

6.定义paimon 表

org.apache.paimon.schema.Schema.Builder builder2 = org.apache.paimon.schema.Schema.newBuilder();
            builder2.column("a", org.apache.paimon.types.DataTypes.STRING());
            builder2.column("b", org.apache.paimon.types.DataTypes.STRING());
            builder2.column("c", org.apache.paimon.types.DataTypes.TIMESTAMP());
            builder2.option("write-buffer-for-append", "true");
            builder2.option("write-buffer-spillable", "false");
        org.apache.paimon.schema.Schema schema2 = builder2.build();

        catalog.createTable(Identifier.create("数据库名", "表名"), schema2, true);

        Table table = catalog.getTable(Identifier.create("数据库名", "表名"));

7.配置sink,并写入

DataType inputType =
                DataTypes.ROW(
                        DataTypes.FIELD("a", DataTypes.STRING()),
                        DataTypes.FIELD("b", DataTypes.STRING()),
                        DataTypes.FIELD("c", DataTypes.TIMESTAMP()));
        FlinkSinkBuilder builder = new FlinkSinkBuilder(table).forRow(input, inputType);

        builder.build();

8.至此,便实现了 flink 读 kafka 写 paimon

<think>我们正在讨论如何优化FlinkSQL实时Paimon非主键表的性能。Paimon是一个流式数据湖存储,支持高效的实时入和查询。对于非主键表,我们需要考虑入性能、小文件问题、数据分布等。优化方向:1.调整入并行度:合理设置并行度可以充分利用集群资源。2.减少小文件:通过调整入参数来减少小文件产生,提高取效率。3.内存与状态管理:合理配置内存和状态大小,避免频繁的Checkpoint和状态过大。4.入参数调优:如调整入缓冲区大小、提交间隔等。5.使用批量入:在适当场景下使用批量入模式。具体优化措施:1.**并行度设置**:在FlinkSQL中,可以通过`SET`命令设置并行度,例如:```sqlSET'parallelism.default' ='8';```或者直接在提交作业时指定。2. **减少小文件**:Paimon通过以下参数控制小文件:-`sink.parallelism`:入并行度,建议与上游一致。- `sink.bucket`:设置桶的数量,可以适当增加以减少每个桶的数据量,但不宜过多。-`compaction`:配置压缩策略,自动合并小文件。可以设置`compaction.min.file-num`和`compaction.max.file-num`来控制压缩触发时机。示例设置:```sqlCREATE TABLEpaimon_table(...) WITH('bucket' ='4', --桶的数量'sink.parallelism'= '8',--入并行度'compaction.min.file-num' ='5', --当小文件数量达到5时触发压缩'compaction.max.file-num'= '50'--每次压缩最多合并50个文件);```3. **内存与状态管理**:-调整Checkpoint间隔:较长的间隔可以减少Checkpoint频率,但会增加恢复时间。需要权衡。-状态后端:使用RocksDB状态后端可以处理更大的状态。-调整内存参数:如`taskmanager.memory.process.size`,根据数据量调整。4.**入参数调优**:- `sink.buffer-flush.max-rows`:缓冲区的最大行数,达到该值则刷新。-`sink.buffer-flush.interval`:缓冲区的刷新间隔。对于非主键表,由于没有主键约束,入可以更高效。但要注意缓冲区设置过大会增加内存压力。示例:```sqlCREATETABLE paimon_table(...) WITH('sink.buffer-flush.max-rows' ='10000','sink.buffer-flush.interval' ='10s');```5. **批量入**:如果数据源是批量的(如Kafka中数据积累一定量),可以开启批量入模式:```sqlSET'execution.runtime-mode' ='batch';```但实时流处理通常使用流模式,因此需要根据实际情况选择。6.**其他优化**:-分区:合理设计分区,避免分区过多导致小文件。-使用动态Bucket:Paimon支持动态调整Bucket数量,通过设置`bucket-key`(即使是非主键表也可以指定一个字段作为bucket的分发字段)来均匀分布数据。示例:创建非主键表并指定bucket-key```sqlCREATE TABLEpaimon_table (user_id STRING,item_idSTRING,behaviorSTRING,dt STRING,hh STRING) WITH('bucket' ='4','bucket-key'= 'user_id', --指定bucket-key,按user_id分桶'sink.parallelism'= '8'); ```7. **压缩优化**:压缩(Compaction)是合并小文件的关键操作,可以调整压缩线程数:```sql'compaction.parallelism'= '2'--压缩任务的并行度```8.**启用LocalMerge**:在入时,可以在本地先进行小文件合并,减少提交到文件系统的文件数。设置:```sql'write.merge-manifest'= 'true'--开启合并manifest文件```9.**调整文件大小**:通过设置目标文件大小来控制入的文件大小:```sql'write.target-file-size'= '128MB' --目标文件大小,默认为128MB```10. **使用Flink SQLTuning**:-开启MiniBatch聚合(如果入前有聚合操作):```sqlSET'table.exec.mini-batch.enabled'= 'true';SET'table.exec.mini-batch.size' ='1000';```-如果入前有窗口操作,可以调整窗口参数。注意:非主键表不支持更新,因此入性能通常比主键表高,但要注意数据去重(如果需要)需要在入前完成。引用说明: [^1]: Paimon官方文档中关于性能调优的部分:https://paimon.apache.org/docs/master/optimization/overview/ §§相关问题§§1.如何监控Paimon表的入性能?2.非主键表与主键表在入性能上有何差异?3.如何解决非主键表的数据重复问题?4.在Paimon表时,如何平衡实时性和性能?</think>### Flink SQL实时Paimon非主键表性能优化指南 #### 1. **并行度优化** - **调整入并行度** 通过`SET`命令动态调整作业并行度,建议与上游数据源分区数对齐: ```sql SET 'parallelism.default' = '8'; -- 根据集群资源调整 ``` - **Bucket分区策略** 在表定义中显式设置`bucket`参数,避免数据倾斜: ```sql CREATE TABLE paimon_table ( ... ) WITH ( 'bucket' = '4', -- 建议设置为并行度的整数倍 'bucket-key' = 'user_id' -- 选择高基数字段 ); ``` #### 2. **小文件治理** - **合并压缩策略** 配置自动压缩减少小文件: ```sql ALTER TABLE paimon_table SET ( 'compaction.min.file-num' = '5', -- 触发压缩的最小文件数 'compaction.max.file-num' = '50', -- 单次压缩最大文件数 'compaction.size' = '128MB' -- 目标文件大小 ); ``` - **入缓冲区优化** 增大内存缓冲区减少IO次数: ```sql SET 'table.exec.sink.buffer-flush.max-rows' = '10000'; -- 缓冲区行数阈值 SET 'table.exec.sink.buffer-flush.interval' = '10s'; -- 刷新间隔 ``` #### 3. **状态管理优化** - **RocksDB状态后端** 在`flink-conf.yaml`中配置: ```yaml state.backend: rocksdb rocksdb.memory.managed: true taskmanager.memory.managed.fraction: 0.7 -- 增大托管内存比例 ``` - **状态TTL清理** 对临时状态设置生存时间: ```sql SET 'table.exec.state.ttl' = '1h'; -- 自动清理过期状态 ``` #### 4. **入模式调优** - **批量提交模式** 启用批量提交减少事务开销: ```sql SET 'execution.batch.interval' = '5s'; SET 'execution.batch.size' = '10000'; ``` - **异步快照机制** 在Checkpoint配置中启用: ```sql SET 'execution.checkpointing.snapshot-compression' = true; SET 'execution.checkpointing.unaligned' = true; -- 非对齐Checkpoint ``` #### 5. **资源分配策略** - **内存精细控制** 针对TM内存配置: ```yaml taskmanager.memory.task.heap.size: 4096m -- 增大堆内存 taskmanager.memory.managed.size: 8192m -- 托管内存 ``` - **网络缓冲区优化** 在`flink-conf.yaml`中调整: ```yaml taskmanager.network.memory.buffer-size: 32mb -- 增大网络缓存 ``` #### 6. **数据分布优化** - **动态分区裁剪** 启用分区过滤减少IO: ```sql SET 'table.optimizer.dynamic-partition-pruning' = true; ``` - **预聚合入** 在SQL中先进行局部聚合: ```sql INSERT INTO paimon_table SELECT window_start, user_id, SUM(amount) FROM TABLE(TUMBLE(TABLE source, DESCRIPTOR(event_time), INTERVAL '1' MINUTE)) GROUP BY window_start, user_id; ``` > **关键原则**:非主键表需重点解决**数据分布均匀性**和**小文件合并效率**问题。通过`bucket-key`控制数据分布,配合压缩参数实现吞吐量与延迟的平衡[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路边草随风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值