Fink SQL 实践之OVER窗口
问题场景
Flink SQL 是一种使用 SQL 语义设计的开发语言,用它解决具体业务需求是一种全新体验,类似于从过程式编程到函数式编程的转变一样,需要一个不断学习和实践的过程。在看完了 Flink 官方文档中 SQL 部分 ,以及官方提供的 SQL Training 后,觉得自己装备了必杀技准备横扫需求了,这时先来一个简单的营销需求:实时计算今天用户加页面维度的浏览次数,即实时输出PV,下游根据某些规则比如今天用户浏览A页面达到N次触发某种动作。当我使用 group by user 并输出到 kafka 中时,发生异常: AppendStreamTableSink requires that Table has only insert changes
,这是因为不带时间窗口的分组聚合不支持流式输出,依赖 Flink Dynamic Tables 的实现原理。修改后加上时间窗口但是不支持实时触发,只在窗口结束时输出一次结果,总之仔细翻了个遍,发现上面资料中都没有类似场景,不会这么简单都实现不了吧?
首先使用标准 SQL 中的 group by 对数据进行分组时,每个分组只能输出一行,其中可以有聚合结果和 group by 的列,如果没有开启 ONLY_FULL_GROUP_BY
模式(MySQL 5.7 开始默认开启),还可以输出任意一行值的非 group by 的列,而 Flink 则是始终开启,不支持非聚合列输出。反过来如果我们想要在聚合计算后输出所有明细,即每行明细带上聚合结果,这时只能使用 OVER窗口和它的分组 Partition(还有一个思路是聚合结果和明细再关联查询)。
此外在 Flink 中提供了一系列时间窗口函数,如滚动窗口 Tumble、滑动窗口 Hop、会话窗口 Session 等,可以将时间窗口作为 group by 分组项之一,但是正如前面所说它们都只能在窗口结束时触发计算输出,无法满足实时触发需求,这点也只能通过 OVER 窗口实现。
OVER 窗口介绍
OVER 窗口是传统数据库的窗口函数,它定义了一行记录向前多少行或向后多少行作为一个窗口,以此范围进行分组 Partion 和聚合计算。初次接触 OVER 窗口觉得不好理解,但实际上它是 SQL 标准支持的特性,包括 Oracle、MySQL、PostgreSQL 等主流数据库都已支持,下面是来自 PostgreSQL 文档 3.5. Window Functions 中的介绍:
A window function performs a calculation across a set of table rows that are somehow related to the current row. This is comparable to the type of calculation that can be done with an aggregate function. However, window functions do not cause rows to become grouped into a single output row like non-window aggregate calls would. Instead, the rows retain their separate identities. Behind the scenes, the window function is able to access more than just the current row of the query result.
其中有具体 SQL 例子辅助理解,此外在维基百科有个更简短介绍 SQL window function ,有了以上基础后再来看 Flink SQL 中的 OVER 窗口,官方文档对 OVER 窗口只有几行描述,SQL Training 中甚至没有涉及,这里我们可以参考阿里云实时计算文档 OVER窗口 ,为了解释后面例子,这里我们只关注其中 Bounded RANGE OVER 类型:具有相同时间值(时间戳毫秒数)的元素视为同一计算行,它对应一个向前有限时间范围的行到该行的一个窗口(因为每一个新到的计算行是该窗口最后一个,以后的还没发生当然无法计算),每一个窗口都会触发一次计算和输出。
OVER 窗口应用示例
首先通过 DDL 定义源数据表和结果表,如下输入是用户行为消息,输出到计算结果消息。
CREATE TABLE `user_action` (
`user_id` VARCHAR,
`page_id` VARCHAR,
`action_type` VARCHAR,
`event_time` TIMESTAMP,
WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
'connector.type' = 'kafka',
'connector.topic' = 'user_action',
'connector.version' = '0.11',
'connector.properties.0.key' = 'bootstrap.servers',
'connector.properties.0.value' = 'xxx:9092',
'connector.startup-mode' = 'latest-offset',
'update-mode' = 'append',
'...' = '...'
);
CREATE TABLE `agg_result` (
`user_id` VARCHAR,
`page_id` VARCHAR,
`result_type` VARCHAR,
`result_value` BIGINT
) WITH (
'connector.type' = 'kafka',
'connector.topic' = 'agg_result',
'...' = '...'
);
场景一,实时触发的最近2小时用户+页面维度的点击量,注意窗口是向前2小时,类似于实时触发的滑动窗口。
insert into
agg_result
select
user_id,
page_id,
'click-type1' as result_type
count(1) OVER (
PARTITION BY user_id, page_id
ORDER BY event_time
RANGE BETWEEN INTERVAL '2' HOUR PRECEDING AND CURRENT ROW
) as result_value
from
user_action
where
action_type = 'click'
场景二,实时触发的当天用户+页面维度的浏览量,这就是开篇问题解法,其中多了一个日期维度分组条件,这样就做到输出结果从滑动时间转为固定时间(根据时间区间分组),因为 WATERMARK 机制,今天并不会有昨天数据到来(如果有都被自动抛弃),因此只会输出今天的分组结果。
insert into
agg_result
select
user_id,
page_id,
'view-type1' as result_type
count(1) OVER (
PARTITION BY user_id, page_id, DATE_FORMAT(event_time, 'yyyyMMdd')
ORDER BY event_time
RANGE BETWEEN INTERVAL '1' DAY PRECEDING AND CURRENT ROW
) as result_value
from
user_action
where
action_type = 'view'
场景三,实时触发的当天用户+页面点击率 CTR(Click-Through-Rate),这相比前面增加了多个 OVER 聚合计算,可以将窗口定义写在最后。注意示例中缺少了类型转换,因为除法结果是 decimal,也缺少精度处理函数 ROUND。
insert into
agg_result
select
user_id,
page_id,
'ctr-type1' as result_type,
sum(
case
when action_type = 'click' then 1 else 0
end
) OVER w
/
if(
sum(
case
when action_type = 'view' then 1 else 0
end
) OVER w = 0,
1,
sum(
case
when action_type = 'view' then 1 else 0
end
) OVER w
)
as result_value
from
user_action
where
1 = 1
WINDOW w AS (
PARTITION BY user_id, page_id, DATE_FORMAT(event_time,'yyyyMMdd')
ORDER BY event_time
RANGE BETWEEN INTERVAL '1' DAY PRECEDING AND CURRENT ROW
)
此外,OVER 窗口聚合还可以支持查询子句、关联查询、UNION ALL 等组合,并可以实现对关联出来的列进行聚合等复杂情况。
OVER 窗口问题和优化
在底层实现中,所有细分 OVER 窗口的数据都是共享的,只存一份,这点不像滑动窗口会保存多份窗口数据。但是 OVER 窗口会把所有数据明细存在状态后端中(内存、RocksDB 或 HDFS),每一次窗口计算后会清除过期数据。因此如果向前窗口时间较大,或数据明细过多,可能会占用大量内存,即使通过 RocksDB 存在磁盘上,也有因为磁盘访问慢导致性能下降进而产生反压问题。在实现源码 RowTimeRangeBoundedPrecedingFunction
可以看到虽然每次窗口计算时新增聚合值和减少过期聚合值是增量式的,不用遍历全部窗口明细,但是为了计算过期数据,即超过 PRECEDING 的数据,仍然需要把存储的那些时间戳全部拿出来遍历,判断是否过期,以及是否要减少聚合值。我们尝试了通过数据有序性减少查询操作,但是效果并不明显,目前主要是配置调优和加大任务分片数进行优化。
Fink SQL 实践之OVER窗口的更多相关文章
- [转]Ionic最佳实践-使用模态窗口modal
本文转自:http://m.blog.csdn.net/blog/betreex/45649689 原文地址:Ionic最佳实践-使用模态窗口modal 模态窗口的结构 在Ionic中,模态窗口通过$ ...
- 【SQL实践】其他常用SQL汇总
[SQL实践]其他常用SQL汇总 1.联表更新 update students stu inner join course on course.STUDENT_ID=stu.id set stu.na ...
- PL/SQL Developer图形化窗口创建数据库(表空间和用户)以及相关查询sql
前言:上一篇安装好oracle和pl/sql后,这篇主要讲如何创建数据库,因为接下来我的项目会连接数据库进行开发. 第一步.先用系统管理员登录pl/sql 我这里系统管理员用户名为system,密码为 ...
- SQL实践中的50句
一个项目涉及到的50个Sql语句(整理版)--1.学生表Student(S,Sname,Sage,Ssex) --S 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别--2.课程 ...
- SQL实践遇到的知识点
聚集函数count() count()统计元组的个数,即行数 count(0).count(1)与count(*)的执行效率是一样的 count(column)与count(*) 如果column中含 ...
- SQL 实践和技巧 <2>
转自 http://i.cnblogs.com/EditPosts.aspx?opt=1 几个小技巧 (1)||的使用: select ‘(‘||phone[1,3]||’)’phone[5, ...
- PhoneGap下Web SQL实践
HTML5里的Web SQL数据库,内置了SQLite数据库, 对数据库的操作使用executeSql执行增删改查 1. 创建数据库 function creatDatabase(){ db = op ...
- PL/SQL的命令行窗口中执行脚本
注意脚本路径中不能有空格, 格式如下:SQL>@D:\1211_Export\all.sql 备注: @后面接本地sql文件的路径及执行文件
- 显示Pl/Sql Developer window list窗口
默认情况下Window List窗口是不显示的,这十分不方便 (一)在菜单项的Tools下的Preference选项中的UserInterface中选择Option,在右边对于的Autosave de ...
随机推荐
- 解决jar包冲突
参考文档: http://www.jianshu.com/p/100439269148 idea plugin: https://www.cnblogs.com/huaxingtianxia/p/57 ...
- 吴裕雄--python学习笔记:爬虫包的更换
python 3.x报错:No module named 'cookielib'或No module named 'urllib2' 1. ModuleNotFoundError: No module ...
- java 字符串转日期格式
/** * 字符串转日期格式 * */ public static Date date(String date_str) { try { Calendar zcal = Calendar.getIns ...
- Git相关命令总结
准备类型命令 生成ssh秘钥(密码可以留空): ssh-keygen 配置用户信息(安装后第一件事): # --systen代表配置系统全局,--global代表配置当前用户全局# 在当前项目中使用其 ...
- 你还记得2017年火爆的VR街机店,这一年他们过得还好吗?
对于当下太过急于成功.一夜暴富的人们来说,似乎总是会急不可耐地去抓住每一个有可能成为大势的风口.在这份普遍存在的浮躁心理下,蕴含着极大的不确定性--既让大众认识到太多的创新产品和服务,也让很多参与者痛 ...
- http客户端如何写
使用wireshark协助,设置网卡本地,设置过滤器:http && (ip.src == 192.168.1.80 && ip.dst == 192.168.1.81 ...
- CS229 Lesson 13 高斯混合模型
课程视频地址:http://open.163.com/special/opencourse/machinelearning.html 课程主页:http://cs229.stanford.edu/ 更 ...
- alibaba开发手册
alibaba开发手册 11.19 强制: 方法参数在定义和传入时,多个参数逗号后边必须加空格. IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 ...
- Apple App签名机制
概览 数字签名 签名机制与验证过程 操作流程 数字签名 摘要算法 将任意长度文本通过一个算法得到一个固定长度的文本. 源文本不同,计算结果必然不同 无法从结果反推源 例如,MD5和SHA算法 非对称加 ...
- Java树结构
今天在项目中,运用到了Java树结构,是在一个查询中,选择树结构例如图片 该结构采用了前段的最新的知识,通过xml结构的数据库,后端Spring的映射实现的. 代码示例: 数据库: <!-- 取 ...