Flink实时去重——外部数据库实现

flink常见的实时去重方案:

  • 基于状态后端
  • 基于HyperLogLog
  • 基于布隆过滤器(BloomFilter)
  • 基于BitMap
  • 基于外部数据库

状态后端、布隆过滤器、bitmap都实现过了,本文实现外部数据库的示例。 外部数据库这里采用redis,因为是key-value数据库,MySQL的话采用唯一键实现也是一样的效果。

实现思路

从数据源读取数据需要去重,去重采用redis进行,flink的app以及redis的准确性问题可以参考flink的端到端精确一次语义,此处不是重点。 数据流转从source到redis,redis的数据类型采用集合(set),具有自动去重的特性。

maven依赖

需要添加redisSink的maven依赖

xml 复制代码
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-redis_2.11</artifactId>
    <version>1.1.5</version>
</dependency>

redisSink

官方文档:bahir.apache.org/docs/flink/...

官方示例:

java 复制代码
public static class RedisExampleMapper implements RedisMapper<Tuple2<String, String>>{

    @Override
    public RedisCommandDescription getCommandDescription() {
        return new RedisCommandDescription(RedisCommand.HSET, "HASH_NAME");
    }

    @Override
    public String getKeyFromData(Tuple2<String, String> data) {
        return data.f0;
    }

    @Override
    public String getValueFromData(Tuple2<String, String> data) {
        return data.f1;
    }
}
FlinkJedisPoolConfig conf = new FlinkJedisPoolConfig.Builder().setHost("127.0.0.1").build();

DataStream<String> stream = ...;
stream.addSink(new RedisSink<Tuple2<String, String>>(conf, new RedisExampleMapper());

可以看到需要实现RedisMapper接口,并且重写三个方法。 示例代码使用的是redis的hash数据类型,而我们需要使用set数据类型。从官方给出的信息(如下图)可知,我们需要使用SADD的redis command。

RedisCommandDescription第一个构造参数需要提供redis command(上图所示右半部分);第二个构造参数为additionalKey参数主要是针对SORTED_SET和HASH结构的,在HASH结构里,这个additionalKey对应hash的key,getKeyFromData方法得到的数据对应hash的field,getValueFromData获取的数据对应hash的value。

而set数据类型是不需要提供additionalKey的,但是需要注意,从source的数据需要插入到同一个set中,那个该set的key需要固定,所以我们此处需要重写的getKeyFromData方法应该返回一个固定的字符串作为set的key。

最终实现的接口:

java 复制代码
package others;

import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisCommand;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisCommandDescription;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisMapper;

/**
 * @projectName: wc
 * @package: others
 * @className: RedisSetMapper
 * @author: NelsonWu
 * @description: TODO
 * @date: 2024/2/25 22:53
 * @version: 1.0
 */
public class RedisSetMapper implements RedisMapper<Tuple2<String, Integer>> {
    @Override
    public RedisCommandDescription getCommandDescription() {
        // redis中set需要使用sadd命令添加值
        return new RedisCommandDescription(RedisCommand.SADD);
    }

    @Override
    public String getKeyFromData(Tuple2<String, Integer> stringIntegerTuple2) {
        return "FlinkDeduplication";  // set的key
    }

    @Override
    public String getValueFromData(Tuple2<String, Integer> stringIntegerTuple2) {
        return stringIntegerTuple2.f1.toString();  // 需要注意,这里只能返回string数据类型
    }
}

redis的相关配置这里就不说了,如果非同一台机器访问redis记得设置密码或者关闭默认的模式,否则flink会抛出异常,以及bind相关的IP。可以参考下这里:zhuanlan.zhihu.com/p/28101275

flink application示例

flink的主程序这边比较简单,把处理完的数据发送给redis即可,redis会自动进行去重。 source此处使用的话集合,即有界流,输入1, 2, 33, 1, 3, 33这几个数据。最终在redis中应该只看到4个数据就是对的。

java 复制代码
package sink;

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.redis.RedisSink;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisPoolConfig;
import org.apache.flink.util.Collector;
import others.RedisSetMapper;


/**
 * @projectName: wc
 * @package: sink
 * @className: sinkRedisDemo
 * @author: NelsonWu
 * @description: TODO
 * @date: 2024/2/25 22:30
 * @version: 1.0
 */
public class sinkRedisDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<Integer> source = env.fromElements(1, 2, 33, 1, 3, 33);

        SingleOutputStreamOperator<Tuple2<String, Integer>> dataStream = source.flatMap(new FlatMapFunction<Integer, Tuple2<String, Integer>>() {
            @Override
            public void flatMap(Integer value, Collector<Tuple2<String, Integer>> out) throws Exception {
                String key = String.valueOf(value);
                Tuple2<String, Integer> tupleValue = Tuple2.of(key, value);
                out.collect(tupleValue);
            }
        });

        FlinkJedisPoolConfig jedisPoolConfig = new FlinkJedisPoolConfig.Builder()
                .setHost("172.20.132.197")
                .setPort(6379)
                .setPassword("redis")
                .build();
        dataStream.addSink(new RedisSink<Tuple2<String, Integer>>(jedisPoolConfig, new RedisSetMapper()));

        env.execute("flink_redis_Deduplication");
    }
}

最终执行结果:

参考

redis安装与配置:zhuanlan.zhihu.com/p/28101275

redis的大数据去重方案:

juejin.cn/post/702783...

zhuanlan.zhihu.com/p/97582268

flink的redisSink以及示例demo:

bahir.apache.org/docs/flink/...

www.cnblogs.com/darange/p/1...

cloud.tencent.com/developer/a...

blog.csdn.net/sucaiwa/art...

相关推荐
Hello.Reader1 天前
在 YARN 上跑 Flink CDC从 Session 到 Yarn Application 的完整实践
大数据·flink
二进制_博客1 天前
eventTime+watermarker+allowedLateness到底窗口关闭时间是什么?
flink·kafka
2501_941403762 天前
Python高性能图像识别与TensorFlow实战分享:深度学习模型优化与批量推理经验
flink
Bug快跑-13 天前
面向高并发场景的多语言异构系统架构演进与性能优化策略深度解析实践分享全过程方法论探索
flink
Hello.Reader4 天前
在 Flink Standalone 集群上运行 Flink CDC从下载到跑起一个 MySQL→Doris 同步任务
大数据·mysql·flink
A尘埃4 天前
Flink实时数据处理
大数据·flink·实时数据处理
expect7g4 天前
Paimon源码解读 -- PartialUpdateMerge
大数据·后端·flink
yumgpkpm4 天前
腾讯云TBDS与CDH迁移常见问题有哪些?建议由CDH迁移到CMP 7.13 平台(类Cloudera CDP,如华为鲲鹏 ARM 版)
hive·hadoop·zookeeper·flink·spark·kafka·hbase
Hello.Reader4 天前
使用 Flink CDC 搭建跨库 Streaming ETLMySQL + Postgres → Elasticsearch 实战
大数据·elasticsearch·flink
❀͜͡傀儡师5 天前
docker 部署Flink和传统部署
docker·容器·flink