下面是一个使用窗口的例子,按说明写入了2条数据,各个窗口的开始和结束时间规则,以及水印的使用,代码如下:
-- 第一条数据09:00:25,第二条数据09:01:10
CREATE TEMPORARY TABLE kafka_test_report (
`my_id` VARCHAR(64),
`event_time` VARCHAR(20),
`my_num` BIGINT,
`ts` AS TO_TIMESTAMP(event_time),
WATERMARK FOR ts AS ts - INTERVAL '3' SECOND
) WITH (
'connector' = 'kafka',
...
);
CREATE TEMPORARY TABLE print_sink (
`window_start` TIMESTAMP(3),
`window_end` TIMESTAMP(3),
`my_id` VARCHAR(64),
`my_value` BIGINT
) WITH (
'connector' = 'print'
);
BEGIN STATEMENT SET;
-- 滚动窗口,09:00:00作为窗口开始时间
INSERT INTO print_sink
SELECT
TUMBLE_START(ts, INTERVAL '1' MINUTE) as window_start,
TUMBLE_END(ts, INTERVAL '1' MINUTE) as window_end,
my_id,
COUNT(1) AS my_num
FROM kafka_test_report
GROUP BY TUMBLE(ts, INTERVAL '1' MINUTE),my_id;
-- 滑动窗口,首个更新窗口08:51:00作为开始时间,09:01:00作为结束时间
INSERT INTO print_sink
SELECT
HOP_START (ts, INTERVAL '60' SECOND, INTERVAL '10' MINUTE) AS window_start,
HOP_END (ts, INTERVAL '60' SECOND, INTERVAL '10' MINUTE) AS window_end,
my_id,
SUM(my_num) AS my_num
FROM kafka_test_report
GROUP BY HOP(ts, INTERVAL '60' SECOND, INTERVAL '10' MINUTE),my_id;
-- 累计窗口,首个更新窗口09:00:00->09:00:40
INSERT INTO print_sink
SELECT
window_start,
window_end,
'101' AS my_id,
SUM(my_num) AS my_num
FROM TABLE(CUMULATE(
TABLE kafka_test_report,
DESCRIPTOR(ts), -- 时间戳
INTERVAL '40' SECOND,
INTERVAL '1' HOUR
))
GROUP BY window_start,window_end;
END;
数据源为Kafka,有2个分区(并发),有1个分区一直没有数据,会导致无法触发窗口结束,导致结果数据异常。解决办法为:
- 根据数据乱序的程度设置合理的offset大小,并保证所有并发都有数据。
- 如果某个源表并发或源表上游partition因没有数据导致窗口始终无法被触发,可在Flink配置中添加table.exec.source.idle-timeout: s来触发窗口结束。table.exec.source.idle-timeout表示某个并发来源如果在指定时间内没有数据进来则临时设为空闲,此时下游任务无需等待该空闲并发源去增长他们的watermark,默认为0表示空闲并发源是禁用的。
综上,Flink解决一个分区没有数据无法触发窗口结束的方式是设置一个过期时间告诉Flink系统这个Partition没数据了,那么计算Watermark的时候就不考虑他了,等他有数据再把他列入计算Watermark的范畴。但如果每个分区都没有数据,还是无法更新watermark,即无法触发下游。此时结束标识数据可使用Sink的RDS延迟输出参数的方式配置。