Java Flink SQL:在GROUP BY查询的结果中重复分组键

Java Flink SQL:在GROUP BY查询的结果中重复分组键,java,apache-kafka,apache-flink,flink-streaming,flink-sql,Java,Apache Kafka,Apache Flink,Flink Streaming,Flink Sql,我想在一个包含GROUPBY语句的表中使用Flink SQL执行一个简单的查询。但是在结果中,GROUPBY语句中指定的列有重复的行。这是因为我使用的是流媒体环境,它不记得状态吗 final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); final StreamTableEnvironment tableEnv = TableEnvironment.getTableEn

我想在一个包含GROUPBY语句的表中使用Flink SQL执行一个简单的查询。但是在结果中,GROUPBY语句中指定的列有重复的行。这是因为我使用的是流媒体环境,它不记得状态吗

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
final StreamTableEnvironment tableEnv = TableEnvironment.getTableEnvironment(env);
// configure Kafka consumer
Properties props = new Properties();
props.setProperty("bootstrap.servers", "localhost:9092"); // Broker default host:port
props.setProperty("group.id", "flink-consumer"); // Consumer group ID

FlinkKafkaConsumer011<BlocksTransactions> flinkBlocksTransactionsConsumer = new FlinkKafkaConsumer011<>(args[0], new BlocksTransactionsSchema(), props);
flinkBlocksTransactionsConsumer.setStartFromEarliest();

DataStream<BlocksTransactions> blocksTransactions = env.addSource(flinkBlocksTransactionsConsumer);


tableEnv.registerDataStream("blocksTransactionsTable", blocksTransactions);

Table sqlResult
        = tableEnv.sqlQuery(
                "SELECT block_hash, count(tx_hash) " +
                "FROM blocksTransactionsTable " +
                "GROUP BY block_hash");

DataStream<Test> resultStream = tableEnv
        .toRetractStream(sqlResult, Row.class)
        .map(t -> {
            Row r = t.f1;
            String field2 = r.getField(0).toString();
            long count = Long.valueOf(r.getField(1).toString());
            return new Test(field2, count);
        })
        .returns(Test.class);

resultStream.print();

resultStream.addSink(new FlinkKafkaProducer011<>("localhost:9092", "TargetTopic", new TestSchema()));

env.execute();
final StreamExecutionEnvironment env=StreamExecutionEnvironment.getExecutionEnvironment();
最终StreamTableEnvironment tableEnv=TableEnvironment.getTableEnvironment(env);
//配置卡夫卡消费者
Properties props=新属性();
props.setProperty(“bootstrap.servers”,“localhost:9092”);//代理默认主机:端口
props.setProperty(“group.id”、“flink consumer”);//消费者组ID
FlinkKafkaConsumer011 flinkBlocksTransactionsConsumer=新FlinkKafkaConsumer011(参数[0],新区块链管理架构(),道具);
flinkBlocksTransactionsConsumer.setStartFromEarliest();
DataStream blocksTransactions=env.addSource(flinkBlocksTransactionsConsumer);
tableEnv.registerDataStream(“区块链稳定”,区块链稳定);
表sqlResult
=tableEnv.sqlQuery(
选择块散列、计数(发送散列)+
“来自区块链稳定”+
“按块分组_散列”);
DataStream resultStream=tablenv
.toRetractStream(sqlResult,Row.class)
.map(t->{
行r=t.f1;
String field2=r.getField(0).toString();
long count=long.valueOf(r.getField(1.toString());
返回新测试(字段2,计数);
})
.returns(Test.class);
resultStream.print();
结果stream.addSink(新FlinkKafkaProducer011(“localhost:9092”,“TargetTopic”,新TestSchema());
execute();
我将GROUPBY语句用于block_哈希列,但我有多次相同的block_哈希。这是打印()的结果:

测试{field2='0x2C4A021D514E4F8F0BEB8F0CE71165230492852487DC7811D06FA77C375B5E1',计数=1} 测试{field2='0x2C4A021D514E4F8F0BEB8F0CE71165230492852487DC7811D06FA77C375B5E1',计数=1} 测试{field2='0x2C4A021D514E4F8F0BEB8F0CE71165230492852487DC7811D06FA77C375B5E1',计数=2} 测试{field2='0x780AADC08C294DA46E174FA287172038BBA7AFACF2DFF41FF0F6DEF03906E60',计数=1} 测试{field2='0x182D31BD491527E1E93C4E4468052707EE90C6A8428308A2BD7B6A4D2E10E53',计数=1} 测试{field2='0x182D31BD491527E1E93C4E4468052707EE90C6A8428308A2BD7B6A4D2E10E53',计数=1}


如何在不使用BatchEnvironment的情况下修复此问题?

在流上运行的
分组查询必须生成更新。考虑下面的例子:

SELECT user, COUNT(*) FROM clicks GROUP BY user;
每次,
单击
表格都会收到一个新行,相应的
用户
的计数需要增加和更新


转换为
数据流
时,这些更新必须在流中编码。Flink使用收回和添加消息来做到这一点。通过调用
tEnv.toRetractStream(table,Row.class)
,您可以将
转换为一个
数据流一个
分组依据
在流上运行的查询必须生成更新。考虑下面的例子:

SELECT user, COUNT(*) FROM clicks GROUP BY user;
每次,
单击
表格都会收到一个新行,相应的
用户
的计数需要增加和更新


转换为
数据流
时,这些更新必须在流中编码。Flink使用收回和添加消息来做到这一点。通过调用
tEnv.toRetractStream(table,Row.class)
,您可以将
转换为一个
数据流感谢它真正帮助了我的回复。我现在明白布尔域了。但是我如何才能只得到行的最后一个结果呢?如果我过滤那些有假值的行,我仍然会得到更新后的值。。。。我无法将结果存储在某个地方以在花费太多时间后进行更新…恐怕您必须自己维护更新。流式查询的一个特点是需要更新结果。如果您有一个有界流,您可以附加一个
ProcessFunction
,它保留每个键的最后一个结果,并在作业终止时发出该结果。我有一个有界流,因此ProcessFunction应该是解决方案。你能帮助我吗?除了这个例子:,我没有找到其他例子。我可以给你一些建议。按分组键上的结果流设置键,并添加存储每个键的行版本的键控状态。还将每个键的事件时间计时器设置为
Long.MAX_VALUE-1
,该计时器将在流终止时调用。在
onTimer
方法中,发出所有结果。但是,我实际上建议以批处理模式进行处理。如果你不需要中间结果,流媒体不会提供任何好处,而且成本更高。谢谢你的回复,它确实帮助了我。我现在明白布尔域了。但是我如何才能只得到行的最后一个结果呢?如果我过滤那些有假值的行,我仍然会得到更新后的值。。。。我无法将结果存储在某个地方以在花费太多时间后进行更新…恐怕您必须自己维护更新。流式查询的一个特点是需要更新结果。如果您有一个有界流,您可以附加一个
ProcessFunction
,它保留每个键的最后一个结果,并在作业终止时发出该结果。我有一个有界流,因此ProcessFunction应该是解决方案。你能帮助我吗?除了这个例子:,我没有找到其他例子。我可以给你一些建议。按分组键上的结果流设置键,并添加存储每个键的行版本的键控状态。还将每个键的事件时间计时器设置为
Long.MAX_VALUE-1
,该计时器将在流终止时调用。在
onTimer
方法中,发出所有结果。但是,我实际上建议以批处理模式进行处理。流式传输不起作用
(+, (Bob, 1)) // add first result for Bob
(+, (Liz, 1)) // add first result for Liz
(-, (Bob, 1)) // remove outdated result for Bob
(+, (Bob, 2)) // add updated result for Bob