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

相关推荐
华农DrLai1 天前
Spark SQL Catalyst 优化器详解
大数据·hive·sql·flink·spark
岁岁种桃花儿1 天前
Flink从入门到上天系列第一篇:搭建第一个Flink程序
大数据·linux·flink·数据同步
Hello.Reader1 天前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
Hello.Reader1 天前
Flink 使用 Amazon S3 读写、Checkpoint、插件选择与性能优化
大数据·flink
Hello.Reader1 天前
Flink 对接 Google Cloud Storage(GCS)读写、Checkpoint、插件安装与生产配置指南
大数据·flink
Hello.Reader1 天前
Flink Kubernetes HA(高可用)实战原理、前置条件、配置项与数据保留机制
贪心算法·flink·kubernetes
wending-Y2 天前
记录一次排查Flink一直重启的问题
大数据·flink
Hello.Reader2 天前
Flink 对接 Azure Blob Storage / ADLS Gen2:wasb:// 与 abfs://(读写、Checkpoint、插件与认证)
flink·flask·azure
Hello.Reader2 天前
Flink 文件系统通用配置默认文件系统与连接数限制实战
vue.js·flink·npm