文章目录
1. FlinkSQL定位
通过SQL开发人员可以只关注业务逻辑,学习成本低,容易理解,而且内置了很多的优化规则,可以简化开发复杂度,通过SQL还能在高层应用上实现真正的批流一体。
Hive SQL,Spark SQL,Flink SQL给开发人员带来了极大便捷,让开发人员只需关注业务场景,而无需关注复杂的API编写。
2. 流与表的对偶性
以下是利用FlinkSQL做CDC的场景,mysql表可以转成CDC流,CDC流又可以落盘成mysql表。
表的重要属性:schema,data,DML操作时间/时间字段
流的重要属性:schema(debezium、canal、ogg),data,processTime/eventTime
流与表具备相同的特征,可以信息无损的相互转换,我称之为流表对偶(duality)性。流与表的对偶性,是flinkSQL的理论基石。而理解流与表的对偶性的前提,是要充分理解FlinkSQL的增量查询和双流join原理,见下节。
参考:https://developer.aliyun.com/article/667566?spm=a2c6h.13262185.0.0.36a07e18Wn3kay
3. 持续查询/增量计算
流上的数据源源不断的流入,我们既不能等所有事件流入结束(永远不会结束)再计算,也不会每次来一条事件就像传统数据库一样将全部事件集合重新整体计算一次。
在持续查询的计算过程中,Apache Flink采用增量计算的方式,也就是每次计算都会将计算结果存储到state中,下一条事件到来的时候利用上次计算的结果和当前的事件进行聚合计算。
如以下案例:
// 求订单总数和所有订单的总金额
select count(id) as cnt,sum(amount)as sumAmount from order_tab;
将count和sum更新到在state中;当最新一条数据到来时,count+1,sum+amount。
4. 回撤流
Flink中,Kafka Source/Sink是非回撤流,Group By是回撤流。所谓回撤流,就是可以更新历史数据的流,更新历史数据并不是将发往下游的历史数据进行更改,要知道,已经发往下游的消息是追不回来的。更新历史数据的含义是,在得知某个Key(接在Key BY / Group By后的字段)对应数据已经存在的情况下,如果该Key对应的数据再次到来,会生成一条delete消息和一条新的insert消息发往下游。
聚合算子和Sink算子都有回撤的概念,但是又不尽相同。聚合算子的回撤用于聚合状态的更新,保证了FlinkSQL持续查询/增量查询的正确语义;Sink算子的回撤则更多的是应用于CDC场景,保证了CDC场景下的append、upsert、retract等语义的正确性。
详情见上一篇文章《回撤流》
5. Flink 1.11关于SQL的增强
5.1 DDL写法
对 DDL 的 WITH 参数相对于 1.10 版本做了简化,从用户视角看上就是简化和规范了参数
Old Key (Flink 1.10) | New Key (Flink 1.11) |
---|---|
connector.type | connector |
connector.url | url |
connector.table | table-name |
connector.driver | driver |
connector.username | username |
connector.password | password |
connector.read.partition.column | scan.partition.column |
connector.read.partition.num | scan.partition.num |
connector.read.partition.lower-bound | scan.partition.lower-bound |
connector.read.partition.upper-bound | scan.partition.upper-bound |
connector.read.fetch-size | scan.fetch-size |
connector.lookup.cache.max-rows | lookup.cache.max-rows |
connector.lookup.cache.ttl | lookup.cache.ttl |
connector.lookup.max-retries | lookup.max-retries |
connector.write.flush.max-rows | sink.buffer-flush.max-rows |
connector.write.flush.interval | sink.buffer-flush.interval |
connector.write.max-retries | sink.max-retries |
5.2 主键
Upsert操作需要主键约束来进行更新,Flink 1.10之前通过Group By语句推断主键,这种方式有一些情况是推断不出主键的,比如Group By UDF(id)
,Flink1.11引入了主键约束语法
-- Flink 1.10
create talbe MyUserTable(
id BIGINT, name STRING, age INT
) WITH (
'connector.type'='jdbc',
'connector.url'='jdbc:mysql://localhost:3306/mydb',
'connector.table'='users'
);
-- upsert write
insert into MyUserTable
select id, max(name), max(age) from T