【大数据面试题】002 Flink 如何实现 Exactly-Once 语义

一步一个脚印,一天一道大数据面试题。

在流式大数据处理框架中,Exactly-Once 语义对于确保每条数据精确地只被消费一次(避免重复读取和丢失读取)非常重要。下面将介绍 Flink 是如何实现 Exactly-Once 语义的。

尽管在程序正常运行、资源充足的情况下实现 Exactly-Once 语义并不难,但实际生产环境中存在各种复杂情况和突发状况,因此为了可靠地实现 Exactly-Once,需要以下容错机制。

数据源(Source)

首先,数据源需要记录“偏移量”,即标记已读取的位置。这样,如果程序重启,可以准确地从未被消费的第一条数据开始读取,既不会多读也不会少读。

Flink 检查点(Checkpoint)

Flink 提供了检查点机制,能够在出现错误时准确恢复数据和操作符状态等。只有通过精确的容错恢复机制,才能实现可靠的 Exactly-Once 语义。

Flink 的检查点机制基于分布式快照技术,定期将作业的状态保存到持久存储中,例如分布式文件系统或远程数据库。当发生故障时,Flink 可以使用最近的检查点进行恢复,确保处理过程的准确性。

数据消费端(Sink)

最后,在数据消费端,需要确保消费者能够支持“事务性”提交,比如使用支持事务的数据库(如 MySQL)进行数据写入。这样,在发生故障时,Flink 可以回滚未完成的事务,并重新执行已提交的事务,从而保证数据的一致性和准确性。

如果无法使用事务性提交,另一种方式是通过幂等性操作来实现 Exactly-Once 语义。例如,可以多次将同一条数据放入一个 Set 集合中,依然保持与第一次放入集合时相同的结果。

代码样例

代码样例来自尚硅谷,Flink EXACTLY_ONCE读写 Kafka

public class KafkaEOSDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 代码中用到hdfs,需要导入hadoop依赖、指定访问hdfs的用户名
        System.setProperty("HADOOP_USER_NAME", "atguigu");
        
        
        // TODO 1、启用检查点,设置为精准一次
        env.enableCheckpointing(5000, CheckpointingMode.EXACTLY_ONCE);
        CheckpointConfig checkpointConfig = env.getCheckpointConfig();
        checkpointConfig.setCheckpointStorage("hdfs://hadoop102:8020/chk");
        checkpointConfig.setExternalizedCheckpointCleanup(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);


        // TODO 2.读取kafka
        KafkaSource<String> kafkaSource = KafkaSource.<String>builder()
                .setBootstrapServers("hadoop102:9092,hadoop103:9092,hadoop104:9092")
                .setGroupId("atguigu")
                .setTopics("topic_1")
                .setValueOnlyDeserializer(new SimpleStringSchema())
                .setStartingOffsets(OffsetsInitializer.latest())
                .build();

        DataStreamSource<String> kafkasource = env
                .fromSource(kafkaSource, WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(3)), "kafkasource");
        

        /**
         * TODO 3.写出到Kafka
         * 精准一次 写入Kafka,需要满足以下条件,缺一不可
         * 1、开启checkpoint
         * 2、sink设置保证级别为 精准一次
         * 3、sink设置事务前缀
         * 4、sink设置事务超时时间: checkpoint间隔 <  事务超时时间  < max的15分钟
         */
        KafkaSink<String> kafkaSink = KafkaSink.<String>builder()
                // 指定 kafka 的地址和端口
                .setBootstrapServers("hadoop102:9092,hadoop103:9092,hadoop104:9092")
                // 指定序列化器:指定Topic名称、具体的序列化
                .setRecordSerializer(
                        KafkaRecordSerializationSchema.<String>builder()
                                .setTopic("ws")
                                .setValueSerializationSchema(new SimpleStringSchema())
                                .build()
                )
                // TODO 3.1 精准一次,开启 2pc
                .setDeliveryGuarantee(DeliveryGuarantee.EXACTLY_ONCE)
                // TODO 3.2 精准一次,必须设置 事务的前缀
                .setTransactionalIdPrefix("atguigu-")
                // TODO 3.3 精准一次,必须设置 事务超时时间: 大于checkpoint间隔,小于 max 15分钟
                .setProperty(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, 10*60*1000+"")
                .build();


        kafkasource.sinkTo(kafkaSink);

        env.execute();
    }
}
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值