flink写parquet解决timestamp时间格式字段问题

背景

        Apache Parquet 是一种开源的列式数据文件格式,旨在实现高效的数据存储和检索。它提供高性能压缩和编码方案(encoding schemes)来批量处理复杂数据,并且受到许多编程语言和分析工具的支持。

        在我们通过flink写入parquet文件的时候,会遇到timestamp时间格式写入的问题。flink官方sdk提供的avro定义转换为parquet文件字段定义是没有timestamp类型的,得另找方法解决。

parquet的时间字段

        parquet字段定义存在如下几种类型:

  • BOOLEAN: 1 bit boolean
  • INT32: 32 bit signed ints
  • INT64: 64 bit signed ints
  • INT96: 96 bit signed ints
  • FLOAT: IEEE 32-bit floating point values
  • DOUBLE: IEEE 64-bit floating point values
  • BYTE_ARRAY: 任意长度 byte 数组

        其中timestamp使用int96类型进行存储,只有使用int96按照指定格式存储时间格式,我们才可以通过hive,sparksql等计算引擎读取正确的时间数据。

flink2parquet

      flink官方提供的写入parquet的处理方式如下:

    String avroSchema = "";

    //构建parquet格式定义
    Schema schema = new Schema.Parser().parse(avroSchema);


    BulkWriter.Factory<GenericRecord> writerFactory = ParquetAvroWriters.forGenericRecord(schema);

    org.apache.flink.connector.file.sink.FileSink fileSink = org.apache.flink.connector.file.sink.FileSink.forBulkFormat(
                    new org.apache.flink.core.fs.Path("hdfs://xxxxx/xx/xx"), writerFactory)
            .withRollingPolicy(OnCheckpointRoll
在 Apache Flink 中,**Sink 本身不直接支持“打 Tag”**(如 Kafka 消息的 tag 或 header),但你可以通过以下方式在 **入外部系统时为数据添加 `tag` 信息**,比如: - 在输出消息体中嵌入 `tag` 字段; - 使用支持消息头的 Sink(如 Kafka)并设置 `headers`; - 利用路由或分类逻辑将不同 tag 的数据入不同目标。 下面以常见场景为例,展示如何在 Flink Sink 中“加 `tag`”,特别是你想加 `ac_validator` 这个 tag。 --- ## ✅ 场景一:将数据入 Kafka,并添加 tag(使用 Header) 如果你想让每条消息都带上 `tag: ac_validator`,可以使用 Kafka Producer 的 **消息头(headers)** 功能。 ### 🐍 Python(PyFlink)示例 ```python from pyflink.datastream import StreamExecutionEnvironment from pyflink.common.typeinfo import Types from pyflink.common.serialization import SimpleStringSchema from pyflink.datastream.connectors.kafka import KafkaSink, KafkaRecordSerializationSchema from pyflink.common.serialization import Encoder env = StreamExecutionEnvironment.get_execution_environment() env.add_jars("file:///path/to/flink-sql-connector-kafka.jar") # 添加 Kafka 连接器 # 示例数据流 stream = env.from_collection( collection=[ '{"user": "alice", "action": "login"}', '{"user": "bob", "action": "register"}' ], type_info=Types.STRING() ) # 自定义序列化 schema,支持添加 header class TaggedKafkaSerializationSchema(KafkaRecordSerializationSchema): def __init__(self, topic: str): self.topic = topic def serialize(self, element: str, context): return { 'topic': self.topic, 'value': element.encode('utf-8'), 'headers': [('tag', b'ac_validator')] # ← 添加 tag } # 创建 Kafka Sink kafka_sink = KafkaSink.builder() \ .set_bootstrap_servers('localhost:9092') \ .set_record_serializer(TaggedKafkaSerializationSchema('user_events')) \ .build() # 入 Kafka stream.sink_to(kafka_sink) # 执行任务 env.execute("Flink Sink with tag: ac_validator") ``` > ✅ 输出到 Kafka 的每条消息都会带有 header: `tag=ac_validator` --- ### 💻 Java 示例(更常用) ```java import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.connector.kafka.sink.KafkaSink; import org.apache.flink.connector.kafka.sink.KafkaRecordSerializationSchema; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.util.Collector; import org.apache.kafka.clients.producer.ProducerRecord; public class FlinkSinkWithTag { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); DataStream<String> stream = env.fromElements( "{\"user\":\"alice\", \"action\":\"login\"}", "{\"user\":\"bob\", \"action\":\"register\"}" ); KafkaSink<String> kafkaSink = KafkaSink.<String>builder() .setBootstrapServers("localhost:9092") .setRecordSerializer(new KafkaRecordSerializationSchema<String>() { @Override public ProducerRecord<byte[], byte[]> serialize(String element, KafkaSinkContext context, Long timestamp) { return new ProducerRecord<>( "user_events", null, timestamp, null, element.getBytes(), java.util.Collections.singletonMap("tag", "ac_validator".getBytes()) // header ); } }) .build(); stream.sinkTo(kafkaSink); env.execute("Flink Sink with tag"); } } ``` --- ## ✅ 场景二:入 JSON 文件时,在每条记录中加入 `tag` 字段 如果你是文件(如 JSON、Parquet),可以在数据流中提前 map 加字段。 ### PyFlink 示例 ```python import json from pyflink.common.serialization import Encoder from pyflink.datastream.connectors.file_system import StreamingFileSink # 原始数据 → 加上 tag 字段 def add_tag(value): data = json.loads(value) data['tag'] = 'ac_validator' data['timestamp'] = '2025-04-05T12:00:00Z' return json.dumps(data) tagged_stream = stream.map(add_tag) # 入文件 file_sink = StreamingFileSink.for_row_format( output_path='./output', encoder=Encoder.simple_string_encoder('UTF-8') ).build() tagged_stream.sink_to(file_sink) ``` > 输出文件内容: ```json {"user":"alice","action":"login","tag":"ac_validator","timestamp":"2025-04-05T12:00:00Z"} ``` --- ## ✅ 场景三:根据 tag 路由到不同的 Sink(动态分发) 你可以根据 `tag` 将数据入不同的 Topic 或目录。 ```python # 分流 split_stream = stream.map(lambda x: (x, 'ac_validator')) # 条件入 ac_validator_sink = KafkaSink.builder()... # 配置 target topic A other_sink = KafkaSink.builder()... # 配置 target topic B # 使用 Side Output 或 Filter 分别处理 valid_stream = stream.filter(lambda x: '"register"' in x).map(lambda x: json.dumps(json.loads(x) | {"tag": "ac_validator"})) valid_stream.sink_to(ac_validator_sink) ``` --- ## ✅ 场景四:使用 Prometheus 监控时添加 tag(Label) 如果你用 Flink 监控指标,也可以加 label: ```java // 注册一个带标签的计数器 Counter validatorCounter = MetricGroup.addCounter("event_count", "ac_validator"); validatorCounter.inc(); ``` 这样在 Prometheus 查询时就可以: ```promql event_count{job="flink", tag="ac_validator"} ``` --- ## ✅ 总结:Flink Sink 如何“加 tag” | 目标系统 | 方法 | 是否推荐 | |--------|------|---------| | Kafka | 使用 `ProducerRecord` 的 `headers` 添加 `tag=ac_validator` | ✅ 强烈推荐 | | 文件系统 | 在 `map()` 阶段向 JSON/CSV 添加 `tag` 字段 | ✅ 推荐 | | 数据库 | 插入时入 `tag` 列(如 `INSERT INTO t(tag, data)`) | ✅ 推荐 | | HTTP API | 请求 body 中包含 `"tag": "ac_validator"` | ✅ 可行 | | 多路输出 | 根据 tag 使用 `sideOutput` 或多个 Sink | ✅ 高级用法 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

路边草随风

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

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

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

打赏作者

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

抵扣说明:

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

余额充值