FLink Function DDL支持(FLink FLIP-79翻译、总结进展)

一、目的

FLIP-79目的在于,支持function DDL,同时考虑SQL语法、语义的正确性,并进一步,可以从外部lib(jar或文件),注册UDF。

Flink DDL是由Chen Shuyi等人在design[1]中进行初步讨论,初步讨论主要集中在Table(表)、Type(类型)和View(视图)上。flip69[2]对DDL进行了扩展,对catalog、database和function(函数)进行了更详细的讨论。function DDL也属于FLIP-69范畴。

经过和社区讨论,我们发现几个正在进行的工作,例如flip64[3]、flip65[4]和flip78[5],他们将直接影响function DDL的SQL语法,因此当前FLIP结合已有的工作,对问题进行清晰的描述,并确保当前设计与已有成果对齐,即对齐不同语言UDF中定义的:temporary objects(临时对象)、type inference(类型接口)

二、修改计划

外部需求(背景)

在深入讨论DDL SQL之前,我们想讨论一下,相关FLIP在Flink runtime function方面主要需求:

1、External lib registration(外部lib注册)

这个需求来自于hive集成,它增强了Flink批处理功能。HQL(HIVE SQL)支持类似的语法如下:

CREATE FUNCTION addfunc AS ‘com.example.hiveserver2.udf.add’ USING JAR ‘hdfs:///path/to/jar’

2、Language Distinction(语言差异)

由于Scala中字节码语言的特殊性,从Scala函数中提取类型信息(TypeInformation)存在一些限制。同时,在table runtime(运行时)中,支持python UDF是另一个正在进行的工作。
因此,SQL语法需要考虑支持多种语言。
例如:Mysql创建函数语法支持这样的语言:

CREATE FUNCTION hello (s CHAR(20))RETURNS CHAR(50)DETERMINISTIC RETURN CONCAT(‘Hello, ‘,s,’!’) LANGUAGE SQL

3、Temporary Function Support(支持注册临时函数)

FLIP-57建议将临时函数和非临时函数分别用于catalog和system。由于temporary function(临时函数)只注册到当前session(会话)。因此需要一个来自DDL的flag(标记),来区分function的解析顺序。

CREATE TEMPORARY FUNCTION addfunc AS ‘com.example.hiveserver2.udf.add’ USING JAR ‘hdfs:///path/to/jar’

4、Function Qualifier(函数限定符)

函数限定符(.)的作用域包括:A、特定的catalog、database B、当前catalog和database。

因此,所有的function DDL都需要支持以下3部分路径(catalog1.db1.addfunc):

CREATE FUNCTION catalog1.db1.addfunc AS ‘com.example.hiveserver2.udf.add’ LANGUAGE JVM

三、Function DDL语法

我们提出function DDL语法如下:

创建函数语句(Create Function Statement)

CREATE [TEMPORARY|SYSTEM] FUNCTION [IF NOT EXISTS] [catalog_name.db_name.]function_name AS identifier [LANGUAGE JVM|PYTHON] [USING JAR|FILE|ARCHIVE ‘resource_path’ [, USING JAR|FILE|ARCHIVE ‘path’]*];

删除函数语句(Drop Function Statement)

DROP [TEMPORARY|SYSTEM] FUNCTION [IF EXISTS] [catalog_name.][db_name.] function_name;

修改函数语句(Alter Function Statement)

ALTER [TEMPORARY|SYSTEM] FUNCTION [IF EXISTS] [catalog_name.][db_name.] function_name RENAME TO new_name;

显示函数语句(Show Function Statement)

SHOW FUNCTION [catalog_name.][db_name]

四、使用场景

我们希望使用function DDL支持尽可能多的使用场景。下面我们列出了一些明显可以实现的场景:

从classpath加载UDF

CREATE TEMPORARY FUNCTION catalog1.db1.func1 AS ‘com.xxx.udf.func1UDF’ LANGUAGE ’JVM’
DROP FUNCTION catalog1.db1.geofence

在本例中,假设UDF class已经在classpath(类路径)中。因此,我们只需要通过反射获取class对象(类对象),以确定它是UDF、UDAF还是UDTF,并将其注册到TableEnvironment中。

从远程资源加载UDF

CREATE FUNCTION catalog1.db1.func2 AS ‘com.xxx.udf.func2UDF’ LANGUAGE JVM USING ‘http://artifactory.uber.internal:4587/artifactory/libs-snapshot-local/com/xxx/xxx/xxx-udf/1.0.1-SNAPSHOT/xxx-udf-1.0.1-20180502.011548-12.jar’

在这种情况下,用户可以使用非本地classpath中的类。在上面的示例中,函数catalog1.db1.func2在一个artifactory的jar中。

使用这种类型的模型,我们可以将用户级逻辑从平台中分离出来。每个团队都可以编写并拥有自己的UDF库。Flink平台只负责将它加载到classpath并使用它。我们将在后面的章节中讨论如何实现它。基本上,resource URL将作为用户库添加到Environment中。它将被添加到JobGraph中,并传送到存储层,例如作业提交之前的HDFS

从远程资源加载python UDF

CREATE FUNCTION catalog1.db1.func3 AS ‘com.xxx.udf.func3UDF’ LANGUAGE ‘PYTHON’ USING ‘http://external.resources/flink-udf.py’

五、新增或更改的公共接口

首先需要做的是,在CatalogFunction接口中添加更多的函数。

public interface CatalogFunction {

  String getClassName();

  Enum getLanguage();  // TODO

  Map<String, String> getProperties();

  CatalogFunction copy();

  Optional<List<String>> getResourcePaths();  // TODO

  Optional<String> getDescription();

  Optional<String> getDetailedDescription();

}

第二步:注册用户Jar,为了支持加载外部lib并从外部lib中创建udf,我们需要在ExecutionEnvironment中添加一个函数来注册外部lib。

/**

 * Register a jar file to load in the Flink job dynamically. The jar file will be added into job graph before job   

 * submission. During runtime, the jar file is loaded into user code class loader automatically.

 *

 * @param jarFile The path of the jar file (e.g., “file://a”, “hdfs://b”, “http://c”)

 */

Public void registerUserJarFile(String jarFile) {

    Path path = new Path(jarFile);

    this.userJars.add(path)

}

在作业提交之前,注册用户jar将被添加到StreamGraph中,然后被添加到JobGraphGenerator的JobGraph中。

六、资源隔离

为了考虑不同session类加载的隔离性,我们可以在{Stream}ExecutionEnvironment中添加一个新的接口。如:

Public void registerUserJarFiles(String classloaderName, String... jarFiles) {

  // ...

}

这个接口可以使用特定key(即classloaderName),注册一组jar文件。在内部,它使用与registerCachedFile()类似的路径,后者使用Flink的Blob服务器将Jar文件分发到runtime运行时。

另外,在RuntimeContext中添加一个新接口,使用在name下注册的Jar文件集,创建并缓存一个自定义userCodeClassLoader。

Public ClassLoader getClassLoaderByName(String classloaderName) {

  // ...

}

在UDF function的代码生成(code generation)过程中,它将把多个jar文件加载到自定义ClassLoader中,并使用反射方式调用该函数

另外,在RuntimeContext实现中,我们将保留自定义ClassLoader的缓存,这样我们就不会多次加载同一个lib库。

七、实施计划

注意:对于不同的语言,实现将是不同的,这里我们只讨论java/scala lib所需的API。

从实现的角度来看,我们希望提供与多种语言支持相一致的函数语法,但是目前我们仅讨论java和scala实现范畴。python udf相关的支持将在FLIP-78的范围内进行讨论和实现。
具体的操作项包括:

Flink 1.10 Release

1、在flink-sql-parser中添加function相关语法。
2、在flink-sql-parser模块中定义SqlCreateFunction和SqlDropFunction
3、桥接DDL和TableEnvironment,将function注册到TableEnvironment中

Flink 1.10后续版本

1、支持从java外部资源加载UDF(即支持USING JAR语句)
2、添加scala function相关的支持

FLIP-65作为Table API udf的类型推断接口, 这个FLIP阻碍了向TableEnvImpl中添加scala function。因此,上述1、2、3目前只支持java语言

一旦flip65完成,就可以继Scala function DDL的工作,并将相应的函数注册到TableEnvImpl中。

八、参考

[1] Flink SQL DDL设计
[2] FLIP-69 Flink SQL DDL增强
[3] FLIP-64支持表模块中的临时对象
[4] FLIP-65新类型推理
[5] FLIP-78 Flink Python UDF环境和依赖项管理

九、社区issue和pr进展(总结,非翻译)

1、支持创建、删除、修改函数语法(FLink 1.10.0可用)

FLINK-7151:支持创建、删除、修改函数语法,已提交对应PR GitHub Pull Request #9689

支持的语句
CREATE FUNCTION [IF NOT EXISTS] [catalog_name.db_name.]function_name AS class_name;
DROP FUNCTION [IF EXISTS] [catalog_name.db_name.]function_name;
ALTER FUNCTION [IF EXISTS] [catalog_name.db_name.]function_name RENAME TO new_name;

CREATE function ‘TOPK’ AS ‘com.xxxx.aggregate.udaf.distinctUdaf.topk.ITopKUDAF’;
INSERT INTO db_sink SELECT id, TOPK(price, 5, ‘DESC’) FROM kafka_source GROUP BY id;

当前issue假定用户已经在classpath中加载了function class。像如何从外部(例如HDFS)动态加载udf库,这样的高级特性在别的issue单独解决。

2、为function DDL添加“USING JAR/FILE/ACHIVE”功能(FLink 1.11.0可用)

FLINK-7151子issue FLINK-14055: 为function DDL添加“USING JAR/FILE/ACHIVE”功能
状态:block and open

3、 在{Stream}ExecutionEnvironment中注册用户jar文件(FLink 1.10.0可用)

FLINK-14319 在{Stream}ExecutionEnvironment中注册用户jar文件
已提交对应PR GitHub Pull Request #9841,投票中。

在{Stream}ExecutionEnvironment中,增加了以下接口:

  • void registerUserJarFile(String jarFile)
  • void registerUserJarFile(String jarFile)
  • void addUserJars(List userJars, JobGraph jobGraph)
  • void registerUserJarFile(String jarFile)
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值