一 Java UDF简介
Java UDF 为用户提供 UDF 编写的 Java 接口,以方便用户使用 Java 语言进行自定义函数的执行。
1.1 UDF编写
使用 Java 代码编写 UDF,UDF 的主入口必须为 evaluate
函数。这一点与 Hive 等其他引擎保持一致。Doris 支持的 Java UDF,同时还是 Hive 支持的 UDF,也就是说,对于用户来讲,Hive UDF 是可以直接迁移至 Doris 的。
例如,我们编写了 AddOne
UDF 来完成对整型输入进行加一的操作。
public class AddOne extends UDF {
public Integer evaluate(Integer value) {
return value == null ? null : value + 1;
}
}
编写完成后,编译打包成jar文件。实现的 jar 包可以放在本地也可以存放在远程服务端通过 HTTP 下载,但必须让每个 FE 和 BE 节点都能获取到 jar 包。
否则将会返回错误状态信息 Couldn't open file ......
。
如果定义的 UDF 中需要加载很大的资源文件,或者希望可以定义全局的 static 变量,可以参照下面的最佳实践。
1.2 创建UDF
CREATE FUNCTION java_udf_add_one(int) RETURNS int PROPERTIES (
"file"="file:///path/to/java-udf-demo-jar-with-dependencies.jar",
"symbol"="org.apache.doris.udf.AddOne",
"always_nullable"="true",
"type"="JAVA_UDF"
);
更多语法帮助可参阅 CREATE FUNCTION.
1.3 使用UDF
用户使用 UDF 必须拥有对应数据库的 SELECT
权限。
UDF 的使用与普通的函数方式一致,唯一的区别在于,内置函数的作用域是全局的,而 UDF 的作用域是 DB 内部。
当链接 session 位于数据内部时,直接使用 UDF 名字会在当前 DB 内部查找对应的 UDF。否则用户需要显示的指定 UDF 的数据库名字,例如 dbName.funcName
。
select java_udf_add_one(5) from tableName;
1.4 删除UDF
drop function java_udf_add_one(int);
二 最佳实践
本示例实现IP归属地查询,使用纯真社区版IP库,因此需要加载离线IP数据库文件,属于加载大的资源文件,因此需要使用static变量实现。
2.1 加载离线IP数据库
ip离线库使用查看:https://github.com/tagphi/czdb-search-java
通过 static 变量将ip离线库加载到内存。
Ip2Region.java
package com.sdpost;
import net.cz88.czdb.DbSearcher;
import net.cz88.czdb.QueryType;
import java.io.InputStream;
import java.util.Properties;
public class Ip2Region {
private static DbSearcher dbSearcher;
static {
// 创建一个Properties对象
Properties prop = new Properties();
// 使用当前类的类加载器来获取资源文件的输入流
InputStream propInputStream = Ip2Region.class.getClassLoader().getResourceAsStream("application.properties");
if (propInputStream != null) {
try {
// 加载属性列表
prop.load(propInputStream);
// 获取属性值
String dbFilePath = prop.getProperty("dbfile.path");
dbSearcher = new DbSearcher(dbFilePath, QueryType.MEMORY, "xxxx");
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("无法找到配置文件");
}
}
public static DbSearcher evaluate() {
return dbSearcher;
}
}
application.properties
dbfile.path= /opt/doris/czdb/cz88_public_v4.czdb
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sdpost</groupId>
<artifactId>ip2region</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>net.cz88</groupId>
<artifactId>czdb-search</artifactId>
<version>1.0.2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.sdpost.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
编译打包,生成ip2region-1.0-jar-with-dependencies.jar
需要将依赖包一块打包到jar,因为doris集群中没有相关的jar
2.2 编写 UDF
新建一个Project,将上面打包的jar包ip2region-1.0-jar-with-dependencies.jar
导入到Project中。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sdpost.udf</groupId>
<artifactId>ip2region_udf</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.hive/hive-exec -->
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>2.3.5</version>
<exclusions>
<exclusion>
<groupId>org.pentaho</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sdpost</groupId>
<artifactId>ip2region</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.sdpost.udf.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
编写UDF类 IpRegion.class
package com.sdpost.udf;
import com.sdpost.Ip2Region;
import net.cz88.czdb.DbSearcher;
import net.cz88.czdb.exception.IpFormatException;
import org.apache.hadoop.hive.ql.exec.UDF;
import java.io.IOException;
public class IpRegion extends UDF {
public String evaluate(String value) throws IpFormatException, IOException {
DbSearcher dbSearcher = Ip2Region.evaluate();
return dbSearcher.search(value);
}
}
编译打包生成 ip2region_udf-1.0.jar
此处不需要包含依赖包,因为doris集群中已经存在
2.3 部署UDF
由于想让资源 jar 包被所有的并发引用,所以需要将上面生成的 ip2region-1.0-jar-with-dependencies.jar
和 ip2region_udf-1.0.jar
放到Doris集群中指定路径 fe/custom_lib
和 be/custom_lib
下面,服务重启之后就可以随着 JVM 的启动加载进来。
2.4 创建UDF
CREATE FUNCTION ip_to_region(string) RETURNS string PROPERTIES (
"symbol"="com.sdpost.udf.IpRegion",
"always_nullable"="true",
"type"="JAVA_UDF"
);
--- 使用UDF
select ip_to_region("8.8.8.8") from tableName;
也可以使用 file:/// 方式自定义
ip2region_udf-1.0.jar
的路径,但是ip2region-1.0-jar-with-dependencies.jar
只能放在custom_lib下。