flink source 同步_Flink深入浅出:JDBC Source从理论到实战

88cf08c7ae7766e1a539da22c37a0bcc.png

——图片来自水彩画吧@白马苏瓦

Flink 1.10之后针对Table API&SQL提供了很多外部连接器,可以使用DDL快速创建外部表,从而可以在Flink中基于SQL直接读取外部数据源。在大数据环境中,有一个很经典的场景是数据交换——即以一定的时间周期把业务库中的数据同步到hive或者hdfs中,下面就先介绍官方的使用方法,再通过源码分析其中的技术细节。

1 表定义

jdbc表的定义跟普通的表定义,区别就在于with中的参数:

CREATE TABLE MyUserTable (  ... ) WITH ( 
'connector.type' = 'jdbc', 
'connector.url' = 'jdbc:mysql://localhost:3306/flink-test', 
'connector.table' = 'jdbc_table_name', 
'connector.driver' = 'com.mysql.jdbc.Driver', 
'connector.username' = 'name', 
'connector.password' = 'password', 
-- 以上都是必填参数 
'connector.read.partition.column' = 'column_name', 
'connector.read.partition.num' = '50', 
'connector.read.partition.lower-bound' = '500', 
'connector.read.partition.upper-bound' = '1000', 
'connector.read.fetch-size' = '100' 
)

password前面的参数都是必填项,后面的connector.read相关的参数都是与读取有关的。如果没有配置这些参数,那么flink不会采用任何分区,无论你的并行度设置的多大,其实只有一个slot在工作,并且执行的是全量数据的查询,这个细节稍后再说

2 表使用

如果想实现数据的抽取同步,一种比较简单的方法就是:

insert overwrite ods.xxx partition (ods_date='2020-01-03') 
select * from jdbc_table_name  
where time>'2020-05-08' and time < '2020-05-09'

这样就实现了数据交换的过程,想法很简单,现实很残酷。如果仔细观察flink ui中各个slot的执行情况就不难发现,无论并行度设置多大,总是其中一个slot执行非常缓慢....

3 原理分析

Flink通过SPI寻找到JDBC对应的TableSource,内部会执行下面的方法创建inputformat,其中164行的query就是真正提交给jdbc执行的代码。

98fab52868991b60cd0f33f4391863b0.png

191911b047801f0f56cec433e696f88f.png

仔细看代码165行,conditionFields传入的值总是为空,因此这坨sql根本不会出现where条件(只有当分区字段存在时,flink才会基于分区字段生成where条件;而我们自己sql中的where条件直接被忽略了)。回头再来说说第二部分我们的查询sql:

select * from jdbc_table_name 
where time>'2020-05-08' and time < '2020-05-09'

会最终形成两个步骤:

1 基于jdbc执行select * from jdbc_table_name

2 在flink中基于time过滤数据

5112abe13f8d9efa0d9a7bae09d04e8f.png

假设现在的并行度为3,也就是3个slot在工作,但是其实只有一个slot执行了查询任务。这样就相当于某个slot需要全表扫描数据,然后在内存中过滤数据,返回我们想要的那部分。如果源表数据量非常大,那这个sql几乎是跑不通的。

一种优化方案是使用flink提供的partition.column字段指定分区字段,然后指定上下界,flink内部会基于上下界和分区个数,形成一个个的分区范围并行查询。

d60d68ffdfdbba8be46bcbbc6e469384.png

比如还是上面的sql,假如我配置为:

'connector.read.partition.column' = 'id', 
'connector.read.partition.num' = '3', 
'connector.read.partition.lower-bound' = '1000', 
'connector.read.partition.upper-bound' = '10000',

那么最终形成的查询sql为:

select * from jdbc_table_name where id between 1000 and 3999; 
select * from jdbc_table_name where id between 4000 and 6999; 
select * from jdbc_table_name where id between 7000 and 9999;

假设flink source的并行度为3,那么”很有可能“每个slot都分配了一个查询任务,三个slot并行查询,这样效率就提升很多了。

19b009a1800ff2f21c2af7cd23f5a299.png

注意,是很有可能!flink中source的分区任务分配并不是均衡的,而是抢占式的,所以我们需要尽量把sql的分区数设置的大一些,才能保证每个slot执行的任务是尽量均衡的。

如果想要利用上面的机制加速数据抽取的速度,可以采用投机取巧的方法:查询目标时间范围内的最大id和最小id,设置为分区字段的上下界。不过这样也不一定能解决问题,如果上下界范围过大,效率一样提不上来。

4 优化实战

由于flink官方仅仅提供了一种基于long类型字段的数字分区方法,因此不能实现基于时间范围的查询,这个时间范围也无法直接下推到jdbc层。所以可以自定义一种split方法,上下界改成时间范围的上下界,中间基于分区数自动计算时间范围。比如时间范围为2020-05-08 00:00:00~2020-05-09 00:00:00,假设分区数为24,那么一个小时就是一个分区范围,这样优化后速度就快很多了。

416858c4d04e17e58b88cfbee1c05c7b.png

如果再考虑的全面点,每天的数据变化是不一样的,往往呈正态分布的趋势。比如白天时间段数据两会很大,夜间数据量往往比较小。如果希望分区的数据量更平均一点,可以使用加权分区方法,把各个时间段的数据量考虑进来,这样就能尽量保证每个分区的数据均衡了。

往期推荐:

Flink深入浅出:Sql Gateway源码分析​mp.weixin.qq.com
2da91db8ae4bd5f3bd06d44178777fed.png
Flink深入浅出:JDBC Connector源码分析​mp.weixin.qq.com
f6997b7e2b7c988608f0e9e289d11c88.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值