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...

相关推荐
诗旸的技术记录与分享5 小时前
Flink-1.19.0源码详解-番外补充3-StreamGraph图
大数据·flink
Edingbrugh.南空11 小时前
Flink MySQL CDC 环境配置与验证
mysql·adb·flink
bxlj_jcj1 天前
深入Flink核心概念:解锁大数据流处理的奥秘
大数据·flink
Edingbrugh.南空1 天前
Flink SQLServer CDC 环境配置与验证
数据库·sqlserver·flink
Edingbrugh.南空1 天前
Flink OceanBase CDC 环境配置与验证
大数据·flink·oceanbase
Edingbrugh.南空2 天前
Flink TiDB CDC 环境配置与验证
大数据·flink·tidb
Edingbrugh.南空2 天前
Flink Postgres CDC 环境配置与验证
大数据·flink
lifallen3 天前
Paimon vs. HBase:全链路开销对比
java·大数据·数据结构·数据库·算法·flink·hbase
expect7g12 天前
新时代多流Join的一个思路----Partial Update
后端·flink
expect7g12 天前
Paimon也有聚合表了?
后端·flink